diff --git a/Makefile b/Makefile index a554b55a..75454a5d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: install coverage test docs help build clean build-arm run-arm run-arm64 build-arm64 build-arm32 +.PHONY: install coverage test docs help build clean unit-test-daemon unit-test unit-test-runtime .DEFAULT_GOAL := help define BROWSER_PYSCRIPT @@ -34,11 +34,16 @@ build-unit-test: cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=1 -DCMAKE_BUILD_TYPE:STRING=Debug cmake --build build --config Debug --target bpftime_runtime_tests -unit-test: ## run catch2 unit tests +unit-test-daemon: + build/daemon/test/bpftime_daemon_tests + +unit-test-runtime: ## run catch2 unit tests make -C runtime/test/bpf && cp runtime/test/bpf/*.bpf.o build/runtime/test/ ./build/runtime/unit-test/bpftime_runtime_tests cd build/runtime/test && ctest -VV +unit-test: unit-test-daemon unit-test-runtime ## run catch2 unit tests + build: ## build the package cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=1 cmake --build build --config Debug diff --git a/daemon/.gitignore b/daemon/.gitignore index f9d87818..022f0bbf 100644 --- a/daemon/.gitignore +++ b/daemon/.gitignore @@ -7,7 +7,7 @@ package.yaml ecli bootstrap .output -bpf-mocker +bpf_tracer victim victim2 test.txt diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index a4f461ab..077dae00 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -1,14 +1,46 @@ # Create a target that builds the ebpf program -add_ebpf_program_target(bpftime_daemon_ebpf_target ${CMAKE_CURRENT_SOURCE_DIR}/bpf-mocker.bpf.c ${CMAKE_CURRENT_BINARY_DIR}/bpf-mocker.bpf.o) +add_ebpf_program_target(bpftime_daemon_ebpf_target ${CMAKE_CURRENT_SOURCE_DIR}/kernel/bpf_tracer.bpf.c ${CMAKE_CURRENT_BINARY_DIR}/bpf_tracer.bpf.o) # Create a target that generated the bpf skeleton -add_bpf_skel_generating_target(bpftime_daemon_ebpf_skel ${CMAKE_CURRENT_BINARY_DIR}/bpf-mocker.bpf.o ${CMAKE_CURRENT_BINARY_DIR}/bpf-mocker.skel.h) +add_bpf_skel_generating_target(bpftime_daemon_ebpf_skel ${CMAKE_CURRENT_BINARY_DIR}/bpf_tracer.bpf.o ${CMAKE_CURRENT_BINARY_DIR}/bpf_tracer.skel.h) add_dependencies(bpftime_daemon_ebpf_skel bpftime_daemon_ebpf_target) -add_executable(bpftime_daemon main.cpp bpf-mocker.cpp handle_bpf_event.cpp) -add_dependencies(bpftime_daemon bpftime_daemon_ebpf_skel libbpf spdlog::spdlog) +add_library(libbpftime_daemon STATIC + user/bpf_tracer.cpp + user/handle_bpf_event.cpp + user/bpftime_driver.cpp +) -target_include_directories(bpftime_daemon PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${LIBBPF_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(bpftime_daemon PRIVATE ${LIBBPF_LIBRARIES} elf z spdlog::spdlog) -set_property(TARGET bpftime_daemon PROPERTY CXX_STANDARD 20) +add_executable(bpftime_daemon + user/main.cpp +) + +add_dependencies(libbpftime_daemon + bpftime_daemon_ebpf_skel + libbpf + spdlog::spdlog + runtime + ) + +target_include_directories(libbpftime_daemon PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${LIBBPF_INCLUDE_DIRS}/uapi + ${LIBBPF_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../vm/include + ${CMAKE_CURRENT_SOURCE_DIR}/../runtime/include +) +target_link_libraries(libbpftime_daemon PRIVATE + ${LIBBPF_LIBRARIES} + elf + z + spdlog::spdlog + runtime + ) +set_property(TARGET libbpftime_daemon PROPERTY CXX_STANDARD 20) + +add_dependencies(bpftime_daemon libbpftime_daemon) +target_link_libraries(bpftime_daemon PRIVATE libbpftime_daemon) + +add_subdirectory(test) diff --git a/daemon/README.md b/daemon/README.md index 6abdff1e..c3661801 100644 --- a/daemon/README.md +++ b/daemon/README.md @@ -1,2 +1,4 @@ -# bpf-mocker +# bpftime daemon: trace and replay eBPF related events +The bpftime daemon is a tool to trace and replay eBPF related events. +It's similar to our syscall server but run together with kernel eBPF. diff --git a/daemon/bpf-mocker-event.h b/daemon/bpf_tracer_event.h similarity index 85% rename from daemon/bpf-mocker-event.h rename to daemon/bpf_tracer_event.h index 1a2d41b8..27fb59fc 100644 --- a/daemon/bpf-mocker-event.h +++ b/daemon/bpf_tracer_event.h @@ -14,6 +14,7 @@ enum event_type { SYS_OPEN, SYS_CLOSE, SYS_BPF, + SYS_IOCTL, SYS_PERF_EVENT_OPEN, BPF_PROG_LOAD_EVENT, }; @@ -46,7 +47,6 @@ struct event { int cpu; // uprobe data - uint64_t offset; char name_or_path[NAME_MAX]; } perf_event_data; @@ -55,11 +55,20 @@ struct event { unsigned int insn_cnt; char prog_name[BPF_OBJ_NAME_LEN]; unsigned int insns[MAX_INSN_SIZE]; + // used as key for later lookup in userspace + unsigned long long insns_ptr; } bpf_loaded_prog; struct { int fd; } close_data; + + struct { + int fd; + unsigned long req; + int data; + int ret; + } ioctl_data; }; }; diff --git a/daemon/daemon_config.hpp b/daemon/daemon_config.hpp deleted file mode 100644 index bc8caabb..00000000 --- a/daemon/daemon_config.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef BPFTIME_DAEMON_CONFIG_HPP -#define BPFTIME_DAEMON_CONFIG_HPP - -#include - -struct env { - pid_t pid; - uid_t uid; - bool verbose; - bool failed; - bool show_open; -}; - -#endif // BPFTIME_DAEMON_CONFIG_HPP diff --git a/daemon/bpf-utils.h b/daemon/kernel/bpf_defs.h similarity index 58% rename from daemon/bpf-utils.h rename to daemon/kernel/bpf_defs.h index 61634606..3d3f4c89 100644 --- a/daemon/bpf-utils.h +++ b/daemon/kernel/bpf_defs.h @@ -1,88 +1,8 @@ -#ifndef BPF_UTILS_H -#define BPF_UTILS_H +#ifndef BPFTIME_BPF_DEFS_H +#define BPFTIME_BPF_DEFS_H #include -#include -#include -#include -#include "bpf-mocker-event.h" - -// filter bpf program pid -const volatile int target_pid = 0; -// current bpf program pid, avoid breaking current process -const volatile int current_pid = 0; -// disable modify bpf program -const volatile bool disable_modify = 0; - -const volatile int uprobe_perf_type = 0; -const volatile int kprobe_perf_type = 0; - -// print event to userspace -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} rb SEC(".maps"); - -// pid & fd for all bpf related fds -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 10240); - __type(key, u64); - __type(value, u64); -} bpf_fd_map SEC(".maps"); - -#define PID_MASK_FOR_PFD 0xffffffff00000000 -#define FD_MASK_FOR_PFD 0x00000000ffffffff -#define MAKE_PFD(pid, fd) (((u64)pid << 32) | fd) - -static __always_inline bool is_bpf_fd(u32 fd) { - u32 pid = bpf_get_current_pid_tgid() >> 32; - u64 key = MAKE_PFD(pid, fd); - u64 *pfd = bpf_map_lookup_elem(&bpf_fd_map, &key); - if (!pfd) { - return false; - } - return true; -} - -static __always_inline void set_bpf_fd_if_positive(u32 fd) { - if (fd < 0) { - return; - } - u32 pid = bpf_get_current_pid_tgid() >> 32; - u64 key = MAKE_PFD(pid, fd); - bpf_map_update_elem(&bpf_fd_map, &key, &key, 0); -} - -static __always_inline void clear_bpf_fd(int fd) { - u32 pid = bpf_get_current_pid_tgid() >> 32; - u64 key = MAKE_PFD(pid, fd); - bpf_map_delete_elem(&bpf_fd_map, &key); -} - -static __always_inline bool filter_target(void) -{ - u64 pid = bpf_get_current_pid_tgid() >> 32; - if (target_pid && pid != target_pid) { - // filter target pid - return false; - } - if (current_pid && pid == current_pid) { - // avoid breaking current process - return false; - } - return true; -} - -static __always_inline struct event* fill_basic_event_info(void) { - struct event *event = bpf_ringbuf_reserve(&rb, sizeof(struct event), 0); - if (!event) { - return NULL; - } - event->pid = bpf_get_current_pid_tgid() >> 32; - bpf_get_current_comm(&event->comm, sizeof(event->comm)); - return event; -} + /* BPF has 10 general purpose 64-bit registers and stack frame. */ #define MAX_BPF_REG __MAX_BPF_REG @@ -189,4 +109,4 @@ static __always_inline struct event* fill_basic_event_info(void) { .off = 0, \ .imm = 0 }) -#endif // BPF_UTILS_H +#endif //BPFTIME_BPF_DEFS_H diff --git a/daemon/kernel/bpf_kernel_config.h b/daemon/kernel/bpf_kernel_config.h new file mode 100644 index 00000000..a129a0c3 --- /dev/null +++ b/daemon/kernel/bpf_kernel_config.h @@ -0,0 +1,36 @@ +#ifndef BPFTIME_KERNEL_CONFIG_H +#define BPFTIME_KERNEL_CONFIG_H + +#include + +#define PATH_LENTH 255 + +// filter bpf program pid +const volatile int target_pid = 0; +// current bpf program pid, avoid breaking current process +const volatile int current_pid = 0; +// enable modify bpf program +const volatile bool enable_replace_prog = 0; +// enable modify uprobe +const volatile bool enable_replace_uprobe = 0; +const char new_uprobe_path[PATH_LENTH] = "\0"; + +const volatile int uprobe_perf_type = 0; +const volatile int kprobe_perf_type = 0; + + +static __always_inline bool filter_target(void) +{ + u64 pid = bpf_get_current_pid_tgid() >> 32; + if (target_pid && pid != target_pid) { + // filter target pid + return false; + } + if (current_pid && pid == current_pid) { + // avoid breaking current process + return false; + } + return true; +} + +#endif // BPFTIME_KERNEL_CONFIG_H \ No newline at end of file diff --git a/daemon/bpf-mocker.bpf.c b/daemon/kernel/bpf_tracer.bpf.c similarity index 68% rename from daemon/bpf-mocker.bpf.c rename to daemon/kernel/bpf_tracer.bpf.c index 0c68ae73..29b4a97e 100644 --- a/daemon/bpf-mocker.bpf.c +++ b/daemon/kernel/bpf_tracer.bpf.c @@ -5,8 +5,8 @@ #include #include #include -#include "bpf-mocker-event.h" -#include "bpf-utils.h" +#include "../bpf_tracer_event.h" +#include "bpf_utils.h" struct open_args_t { const char *fname; @@ -17,7 +17,7 @@ struct open_args_t { struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); - __type(key, u32); + __type(key, u64); __type(value, struct open_args_t); } open_param_start SEC(".maps"); @@ -32,8 +32,8 @@ int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter *ctx) args.fname = (const char *)ctx->args[0]; args.flags = (int)ctx->args[1]; - u32 pid = bpf_get_current_pid_tgid() >> 32; - bpf_map_update_elem(&open_param_start, &pid, &args, 0); + u64 pid_tgid = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&open_param_start, &pid_tgid, &args, 0); return 0; } @@ -48,8 +48,8 @@ int tracepoint__syscalls__sys_enter_openat(struct trace_event_raw_sys_enter *ctx args.fname = (const char *)ctx->args[1]; args.flags = (int)ctx->args[2]; - u32 pid = bpf_get_current_pid_tgid() >> 32; - bpf_map_update_elem(&open_param_start, &pid, &args, 0); + u64 pid_tgid = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&open_param_start, &pid_tgid, &args, 0); return 0; } @@ -57,13 +57,13 @@ static __always_inline int trace_exit_open(struct trace_event_raw_sys_exit *ctx) { struct event *event = NULL; struct open_args_t *ap = NULL; - u32 pid = 0; + u64 pid_tgid = 0; if (!filter_target()) { return 0; } - pid = bpf_get_current_pid_tgid() >> 32; - ap = bpf_map_lookup_elem(&open_param_start, &pid); + pid_tgid = bpf_get_current_pid_tgid(); + ap = bpf_map_lookup_elem(&open_param_start, &pid_tgid); if (!ap) return 0; /* missed entry */ @@ -82,7 +82,7 @@ static __always_inline int trace_exit_open(struct trace_event_raw_sys_exit *ctx) /* emit event */ bpf_ringbuf_submit(event, 0); cleanup: - bpf_map_delete_elem(&open_param_start, &pid); + bpf_map_delete_elem(&open_param_start, &pid_tgid); return 0; } @@ -107,13 +107,13 @@ struct bpf_args_t { struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); - __type(key, u32); + __type(key, u64); __type(value, struct bpf_args_t); } bpf_param_start SEC(".maps"); static bool should_modify_program(union bpf_attr *new_attr) { - if (new_attr->insn_cnt < 2 || disable_modify) { + if (new_attr->insn_cnt < 2 || !enable_replace_prog) { return false; } // Only target BPF_PROG_TYPE_KPROBE and BPF_PROG_TYPE_TRACEPOINT @@ -144,6 +144,7 @@ static int process_bpf_prog_load_events(union bpf_attr *attr) event->bpf_loaded_prog.insn_cnt); event->bpf_loaded_prog.insn_cnt = insn_cnt; event->bpf_loaded_prog.type = new_attr.prog_type; + event->bpf_loaded_prog.insns_ptr = (u64)insns; // copy name of the program *((__uint128_t *)&event->bpf_loaded_prog.prog_name) = *((__uint128_t *)&new_attr.prog_name); @@ -189,7 +190,7 @@ static int process_bpf_syscall_enter(struct trace_event_raw_sys_enter *ctx) union bpf_attr *attr = (union bpf_attr *)ctx->args[1]; unsigned int size = (unsigned int)ctx->args[2]; - if (!attr || size < sizeof(*attr)) { + if (!attr) { return 0; } @@ -210,11 +211,11 @@ int tracepoint__syscalls__sys_enter_bpf(struct trace_event_raw_sys_enter *ctx) struct bpf_args_t args = {}; args.cmd = (u32)ctx->args[0]; bpf_probe_read_user(&args.attr, sizeof(args.attr), - (void *)ctx->args[1]); + (void *)ctx->args[1]); args.attr_size = (u32)ctx->args[2]; - u32 pid = bpf_get_current_pid_tgid() >> 32; - bpf_map_update_elem(&bpf_param_start, &pid, &args, 0); + u64 pid_tgid = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&bpf_param_start, &pid_tgid, &args, 0); process_bpf_syscall_enter(ctx); return 0; @@ -224,7 +225,7 @@ static int process_bpf_syscall_exit(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size, int ret, struct trace_event_raw_sys_exit *ctx) { - if (!attr || size < sizeof(*attr)) { + if (!attr) { return 0; } @@ -238,6 +239,9 @@ static int process_bpf_syscall_exit(enum bpf_cmd cmd, union bpf_attr *attr, case BPF_LINK_CREATE: set_bpf_fd_if_positive(ret); break; + case BPF_BTF_LOAD: + set_bpf_fd_if_positive(ret); + break; default: break; } @@ -249,13 +253,12 @@ int tracepoint__syscalls__sys_exit_bpf(struct trace_event_raw_sys_exit *ctx) { struct event *event = NULL; struct bpf_args_t *ap = NULL; - u32 pid = 0; if (!filter_target()) { return 0; } - pid = bpf_get_current_pid_tgid() >> 32; - ap = bpf_map_lookup_elem(&bpf_param_start, &pid); + u64 pid_tgid = bpf_get_current_pid_tgid(); + ap = bpf_map_lookup_elem(&bpf_param_start, &pid_tgid); if (!ap) return 0; /* missed entry */ @@ -264,8 +267,8 @@ int tracepoint__syscalls__sys_exit_bpf(struct trace_event_raw_sys_exit *ctx) return 0; } event->type = SYS_BPF; - bpf_probe_read(&event->bpf_data.attr, - sizeof(event->bpf_data.attr), &ap->attr); + bpf_probe_read(&event->bpf_data.attr, sizeof(event->bpf_data.attr), + &ap->attr); event->bpf_data.attr_size = ap->attr_size; event->bpf_data.bpf_cmd = ap->cmd; event->bpf_data.ret = ctx->ret; @@ -277,28 +280,41 @@ int tracepoint__syscalls__sys_exit_bpf(struct trace_event_raw_sys_exit *ctx) /* emit event */ bpf_ringbuf_submit(event, 0); cleanup: - bpf_map_delete_elem(&bpf_param_start, &pid); + bpf_map_delete_elem(&bpf_param_start, &pid_tgid); return 0; } +char old_uprobe_path[PATH_LENTH] = "\0"; +struct perf_event_attr new_attr = {}; + static __always_inline int process_perf_event_open_enter(struct trace_event_raw_sys_enter *ctx) { struct perf_event_attr *attr = (struct perf_event_attr *)ctx->args[0]; - struct perf_event_attr new_attr = {}; if (!attr) { return 0; } bpf_probe_read_user(&new_attr, sizeof(new_attr), attr); - // if (new_attr.type == uprobe_perf_type) { - // // found uprobe - // char new_path[] = "/home/yunwei/bpftime/benchmark/syscall/victim"; - // new_attr.probe_offset = 0; - // bpf_probe_write_user(attr, &new_attr, sizeof(new_attr)); - // bpf_probe_write_user(&new_attr.uprobe_path, &new_path, - // sizeof(new_path)); - // return 0; - // } + if (new_attr.type == uprobe_perf_type) { + // found uprobe + if (enable_replace_uprobe) { + new_attr.probe_offset = 0; + int size = bpf_probe_read_user_str( + old_uprobe_path, sizeof(old_uprobe_path), + (void *)new_attr.uprobe_path); + if (size <= 0) { + // no uprobe path + return 0; + } + if (size > PATH_LENTH) { + size = PATH_LENTH; + } + bpf_probe_write_user((void *)new_attr.uprobe_path, + new_uprobe_path, size); + bpf_probe_write_user(attr, &new_attr, sizeof(new_attr)); + } + return 0; + } return 0; } @@ -306,12 +322,15 @@ struct perf_event_args_t { struct perf_event_attr attr; int pid; int cpu; + + // we may modify the offset and name, so we keep it here + char name_or_path[NAME_MAX]; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); - __type(key, u32); + __type(key, u64); __type(value, struct perf_event_args_t); } perf_event_open_param_start SEC(".maps"); @@ -323,17 +342,18 @@ int tracepoint__syscalls__sys_enter_perf_event_open( if (!filter_target()) { return 0; } - /* store arg info for later lookup */ /* store arg info for later lookup */ struct perf_event_args_t args = {}; bpf_probe_read_user(&args.attr, sizeof(args.attr), - (void *)ctx->args[0]); + (void *)ctx->args[0]); args.pid = (int)ctx->args[1]; args.cpu = (int)ctx->args[2]; + bpf_probe_read_user_str(args.name_or_path, PATH_LENTH, + (const void *)args.attr.uprobe_path); - u32 pid = bpf_get_current_pid_tgid() >> 32; - bpf_map_update_elem(&perf_event_open_param_start, &pid, &args, 0); + u64 pid_tgid = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&perf_event_open_param_start, &pid_tgid, &args, 0); process_perf_event_open_enter(ctx); return 0; @@ -345,16 +365,15 @@ int tracepoint__syscalls__sys_exit_perf_event_open( { struct event *event = NULL; struct perf_event_args_t *ap = NULL; - u32 pid = 0; if (!filter_target()) { return 0; } - pid = bpf_get_current_pid_tgid() >> 32; - ap = bpf_map_lookup_elem(&perf_event_open_param_start, &pid); + u64 pid_tgid = bpf_get_current_pid_tgid(); + ap = bpf_map_lookup_elem(&perf_event_open_param_start, &pid_tgid); if (!ap) return 0; /* missed entry */ - + set_bpf_fd_if_positive(ctx->ret); /* event data */ @@ -365,15 +384,17 @@ int tracepoint__syscalls__sys_exit_perf_event_open( event->type = SYS_PERF_EVENT_OPEN; bpf_probe_read(&event->perf_event_data.attr, - sizeof(event->perf_event_data.attr), &ap->attr); + sizeof(event->perf_event_data.attr), &ap->attr); event->perf_event_data.pid = ap->pid; event->perf_event_data.cpu = ap->cpu; event->perf_event_data.ret = ctx->ret; + bpf_probe_read(event->perf_event_data.name_or_path, PATH_LENTH, + ap->name_or_path); /* emit event */ bpf_ringbuf_submit(event, 0); cleanup: - bpf_map_delete_elem(&bpf_param_start, &pid); + bpf_map_delete_elem(&perf_event_open_param_start, &pid_tgid); return 0; } @@ -399,7 +420,75 @@ int tracepoint__syscalls__sys_enter_close(struct trace_event_raw_sys_enter *ctx) event->close_data.fd = fd; /* emit event */ bpf_ringbuf_submit(event, 0); - + clear_bpf_fd(fd); + return 0; +} + +struct ioctl_args_t { + int fd; + unsigned long req; + int data; +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, u64); + __type(value, struct ioctl_args_t); +} ioctl_param_start SEC(".maps"); + +SEC("tracepoint/syscalls/sys_enter_ioctl") +int tracepoint__syscalls__sys_enter_ioctl(struct trace_event_raw_sys_enter *ctx) +{ + struct event *event; + if (!filter_target()) { + return 0; + } + int fd = (int)ctx->args[0]; + if (!is_bpf_fd(fd)) { + return 0; + } + /* store arg info for later lookup */ + struct ioctl_args_t args = {}; + args.fd = fd; + args.req = (int)ctx->args[1]; + args.data = (int)ctx->args[2]; + + u64 pid_tgid = bpf_get_current_pid_tgid(); + bpf_map_update_elem(&ioctl_param_start, &pid_tgid, &args, 0); + return 0; +} + +SEC("tracepoint/syscalls/sys_exit_ioctl") +int tracepoint__syscalls__sys_exit_ioctl(struct trace_event_raw_sys_exit *ctx) +{ + struct event *event = NULL; + struct ioctl_args_t *ap = NULL; + + if (!filter_target()) { + return 0; + } + u64 pid_tgid = bpf_get_current_pid_tgid(); + ap = bpf_map_lookup_elem(&ioctl_param_start, &pid_tgid); + if (!ap) + return 0; /* missed entry */ + + /* event data */ + event = fill_basic_event_info(); + if (!event) { + return 0; + } + event->type = SYS_IOCTL; + + event->ioctl_data.data = ap->data; + event->ioctl_data.fd = ap->fd; + event->ioctl_data.req = ap->req; + event->ioctl_data.ret = ctx->ret; + + /* emit event */ + bpf_ringbuf_submit(event, 0); +cleanup: + bpf_map_delete_elem(&ioctl_param_start, &pid_tgid); return 0; } diff --git a/daemon/kernel/bpf_utils.h b/daemon/kernel/bpf_utils.h new file mode 100644 index 00000000..8bc048ad --- /dev/null +++ b/daemon/kernel/bpf_utils.h @@ -0,0 +1,65 @@ +#ifndef BPF_UTILS_H +#define BPF_UTILS_H + +#include +#include "bpf_defs.h" +#include +#include +#include +#include "../bpf_tracer_event.h" +#include "bpf_kernel_config.h" + +// print event to userspace +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +static __always_inline struct event* fill_basic_event_info(void) { + struct event *event = bpf_ringbuf_reserve(&rb, sizeof(struct event), 0); + if (!event) { + return NULL; + } + event->pid = bpf_get_current_pid_tgid() >> 32; + bpf_get_current_comm(&event->comm, sizeof(event->comm)); + return event; +} + +// pid & fd for all bpf related fds +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 10240); + __type(key, u64); + __type(value, u64); +} bpf_fd_map SEC(".maps"); + +#define PID_MASK_FOR_PFD 0xffffffff00000000 +#define FD_MASK_FOR_PFD 0x00000000ffffffff +#define MAKE_PFD(pid, fd) (((u64)pid << 32) | fd) + +static __always_inline bool is_bpf_fd(u32 fd) { + u32 pid = bpf_get_current_pid_tgid() >> 32; + u64 key = MAKE_PFD(pid, fd); + u64 *pfd = bpf_map_lookup_elem(&bpf_fd_map, &key); + if (!pfd) { + return false; + } + return true; +} + +static __always_inline void set_bpf_fd_if_positive(u32 fd) { + if (fd < 0) { + return; + } + u32 pid = bpf_get_current_pid_tgid() >> 32; + u64 key = MAKE_PFD(pid, fd); + bpf_map_update_elem(&bpf_fd_map, &key, &key, 0); +} + +static __always_inline void clear_bpf_fd(int fd) { + u32 pid = bpf_get_current_pid_tgid() >> 32; + u64 key = MAKE_PFD(pid, fd); + bpf_map_delete_elem(&bpf_fd_map, &key); +} + +#endif // BPF_UTILS_H diff --git a/daemon/test/CMakeLists.txt b/daemon/test/CMakeLists.txt new file mode 100644 index 00000000..726c09ee --- /dev/null +++ b/daemon/test/CMakeLists.txt @@ -0,0 +1,48 @@ +Include(FetchContent) +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.0.1 +) + +FetchContent_MakeAvailable(Catch2) + +find_package(Boost REQUIRED) + +set(TEST_SOURCES + test_daemon.cpp + test_daemon_driver.cpp +) + +add_executable(bpftime_daemon_tests ${TEST_SOURCES}) +set_property(TARGET bpftime_daemon_tests PROPERTY CXX_STANDARD 20) +add_dependencies(bpftime_daemon_tests runtime bpftime-object) +target_link_libraries(bpftime_daemon_tests PRIVATE +runtime +bpftime-object +Catch2::Catch2WithMain +libbpftime_daemon +) +target_include_directories(bpftime_daemon_tests + PRIVATE + ${BPFTIME_RUNTIME_INCLUDE} + ${BPFTIME_OBJECT_INCLUDE_DIRS} + ${Catch2_INCLUDE} + ${Boost_INCLUDE} + ../ +) +add_test(NAME bpftime_daemon_tests COMMAND bpftime_daemon_tests) + +# These are necessary ebpf program required by the test +# set(used_ebpf_programs +# uprobe +# # replace # Not used now +# filter) + +foreach(current ${used_ebpf_programs}) + set(curr_target_name bpftime_test_ebpf_prog_${current}) + add_ebpf_program_target(${curr_target_name} ${CMAKE_CURRENT_SOURCE_DIR}/assets/${current}.bpf.c ${CMAKE_CURRENT_BINARY_DIR}/${current}.bpf.o) + add_dependencies(bpftime_runtime_tests ${curr_target_name}) + string(TOUPPER ${current} current_name_upper) + target_compile_definitions(bpftime_runtime_tests PRIVATE EBPF_PROGRAM_PATH_${current_name_upper}=${CMAKE_CURRENT_BINARY_DIR}/${current}.bpf.o) +endforeach() diff --git a/daemon/test/common_def.hpp b/daemon/test/common_def.hpp new file mode 100644 index 00000000..302b0049 --- /dev/null +++ b/daemon/test/common_def.hpp @@ -0,0 +1,23 @@ +#ifndef _COMMON_DEF_HPP +#define _COMMON_DEF_HPP +#include +#include +struct shm_remove { + std::string filename; + shm_remove(const std::string &&filename) : filename(filename) + { + boost::interprocess::shared_memory_object::remove( + filename.c_str()); + } + + ~shm_remove() + { + boost::interprocess::shared_memory_object::remove( + filename.c_str()); + } +}; + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +#endif diff --git a/runtime/unit-test/test_daemon.cpp b/daemon/test/test_daemon.cpp similarity index 100% rename from runtime/unit-test/test_daemon.cpp rename to daemon/test/test_daemon.cpp diff --git a/daemon/test/test_daemon_driver.cpp b/daemon/test/test_daemon_driver.cpp new file mode 100644 index 00000000..15059c5b --- /dev/null +++ b/daemon/test/test_daemon_driver.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include "bpftime_shm.hpp" +#include "bpftime_shm_internal.hpp" +#include "user/bpftime_driver.hpp" +#if !defined(__x86_64__) && defined(_M_X64) +#error Only supports x86_64 +#endif +using namespace bpftime; + +#define TEST_INSN_SIZE 12 + +TEST_CASE("Test daemon driver") +{ + daemon_config cfg; + char insns[12 * sizeof(ebpf_inst)]; + int pid_0 = 0; + int pid_1 = 1; + int id; + + bpftime_driver driver(cfg); + id = driver.bpftime_progs_create_server(pid_0, 1, + (const ebpf_inst *)insns, + TEST_INSN_SIZE, + "test_prog1", 0); + REQUIRE(id >= 0); + id = driver.bpftime_progs_create_server(pid_1, 1, + (const ebpf_inst *)insns, + TEST_INSN_SIZE, + "test_prog2", 0); + REQUIRE(id >= 0); +} diff --git a/daemon/bpf-mocker.cpp b/daemon/user/bpf_tracer.cpp similarity index 72% rename from daemon/bpf-mocker.cpp rename to daemon/user/bpf_tracer.cpp index 4e2ad50c..ee4cae51 100644 --- a/daemon/bpf-mocker.cpp +++ b/daemon/user/bpf_tracer.cpp @@ -1,4 +1,4 @@ -// Description: bpf-mocker daemon +// Description: bpf_tracer daemon #include #include #include @@ -10,11 +10,14 @@ #include #include #include -#include "bpf-mocker-event.h" -#include "bpf-mocker.skel.h" +#include "bpf_tracer_event.h" +#include "bpf_tracer.skel.h" #include "daemon_config.hpp" #include "handle_bpf_event.hpp" #include "daemon.hpp" +#include +#include +#include #define NSEC_PER_SEC 1000000000ULL @@ -22,7 +25,6 @@ using namespace bpftime; static volatile sig_atomic_t exiting = 0; static bool verbose = false; -static bpf_event_handler handler({}); static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) @@ -41,26 +43,26 @@ static void sig_int(int signo) static int handle_event_rb(void *ctx, void *data, size_t data_sz) { const struct event *e = (const struct event *)data; - handler.handle_event(e); + bpf_event_handler* handler = (bpf_event_handler*)ctx; + assert(handler != NULL); + handler->handle_event(e); return 0; } -void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) -{ - fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu); -} - -int bpftime::start_daemon(struct env env) +int bpftime::start_daemon(struct daemon_config env) { LIBBPF_OPTS(bpf_object_open_opts, open_opts); struct ring_buffer *rb = NULL; - struct bpf_mocker_bpf *obj = NULL; + struct bpf_tracer_bpf *obj = NULL; int err; + spdlog::cfg::load_env_levels(); + libbpf_set_print(libbpf_print_fn); + bpftime_driver driver(env); // update handler config - handler = bpf_event_handler(env); + bpf_event_handler handler = bpf_event_handler(env, driver); verbose = env.verbose; if (signal(SIGINT, sig_int) == SIG_ERR) { @@ -70,32 +72,35 @@ int bpftime::start_daemon(struct env env) goto cleanup; } - obj = bpf_mocker_bpf__open(); + obj = bpf_tracer_bpf__open(); if (!obj) { + err = -1; fprintf(stderr, "failed to open BPF object\n"); goto cleanup; } /* initialize global data (filtering options) */ obj->rodata->target_pid = env.pid; - obj->rodata->disable_modify = true; + obj->rodata->enable_replace_prog = env.enable_replace_prog; + strncpy(obj->rodata->new_uprobe_path, env.new_uprobe_path, PATH_LENTH); + obj->rodata->enable_replace_uprobe = env.enable_replace_uprobe; obj->rodata->uprobe_perf_type = determine_uprobe_perf_type(); obj->rodata->kprobe_perf_type = determine_kprobe_perf_type(); - err = bpf_mocker_bpf__load(obj); + err = bpf_tracer_bpf__load(obj); if (err) { fprintf(stderr, "failed to load BPF object: %d\n", err); goto cleanup; } - err = bpf_mocker_bpf__attach(obj); + err = bpf_tracer_bpf__attach(obj); if (err) { fprintf(stderr, "failed to attach BPF programs\n"); goto cleanup; } /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(obj->maps.rb), handle_event_rb, NULL, + rb = ring_buffer__new(bpf_map__fd(obj->maps.rb), handle_event_rb, &handler, NULL); if (!rb) { err = -1; @@ -117,7 +122,7 @@ int bpftime::start_daemon(struct env env) cleanup: ring_buffer__free(rb); - bpf_mocker_bpf__destroy(obj); + bpf_tracer_bpf__destroy(obj); return err != 0; } diff --git a/daemon/user/bpftime_driver.cpp b/daemon/user/bpftime_driver.cpp new file mode 100644 index 00000000..cd01d80b --- /dev/null +++ b/daemon/user/bpftime_driver.cpp @@ -0,0 +1,183 @@ +#include "bpftime_driver.hpp" +#include "ebpf-vm.h" +#include +#include "bpftime_shm.hpp" + +using namespace bpftime; +using namespace std; + +int bpftime_driver::find_minimal_unused_id() +{ + int id = 0; + while (1) { + bool find = false; + for (auto [pid_fd, idx] : pid_fd_to_id_map) { + if (id == idx) { + find = true; + } + } + if (find) { + id++; + } else { + break; + } + } + return id; +} + +int bpftime_driver::bpftime_link_create_server(int server_pid, int fd, + int prog_fd, int target_fd) +{ + int id = find_minimal_unused_id(); + int fd_id = check_and_get_pid_fd(server_pid, fd); + if (fd_id < 0) { + spdlog::error("fd {} for pid {} not exists", fd, server_pid); + return -1; + } + int prog_id = check_and_get_pid_fd(server_pid, prog_fd); + if (prog_id < 0) { + spdlog::error("prog fd {} for pid {} not exists", prog_fd, + server_pid); + return -1; + } + + int res = bpftime_link_create(id, prog_id, fd_id); + if (res < 0) { + spdlog::error("Failed to create link for id {}", id); + return -1; + } + pid_fd_to_id_map[get_pid_fd_key(server_pid, fd)] = id; + spdlog::info("create link {} for pid {} fd {}", id, server_pid, fd); + return id; +} + +int bpftime_driver::bpftime_progs_create_server(int server_pid, int fd, + const ebpf_inst *insn, + size_t insn_cnt, + const char *prog_name, + int prog_type) +{ + int id = find_minimal_unused_id(); + int res = + bpftime_progs_create(id, insn, insn_cnt, prog_name, prog_type); + if (res < 0) { + spdlog::error("Failed to create prog for id {}", id); + return -1; + } + pid_fd_to_id_map[get_pid_fd_key(server_pid, fd)] = id; + spdlog::info("create prog {} for pid {} fd {}", id, server_pid, fd); + return id; +} + +int bpftime_driver::bpftime_maps_create_server(int server_pid, int fd, + const char *name, + bpftime::bpf_map_attr attr) +{ + int id = find_minimal_unused_id(); + int res = bpftime_maps_create(id, name, attr); + if (res < 0) { + spdlog::error("Failed to create map for id {}", id); + return -1; + } + pid_fd_to_id_map[get_pid_fd_key(server_pid, fd)] = id; + spdlog::info("create map {} for pid {} fd {}", id, server_pid, fd); + return id; +} + +int bpftime_driver::bpftime_attach_perf_to_bpf_server(int server_pid, + int perf_fd, int bpf_fd) +{ + int perf_id = check_and_get_pid_fd(server_pid, perf_fd); + if (perf_id < 0) { + spdlog::error("perf fd {} for pid {} not exists", perf_fd, + server_pid); + return -1; + } + int bpf_id = check_and_get_pid_fd(server_pid, bpf_fd); + if (bpf_id < 0) { + spdlog::error("bpf fd {} for pid {} not exists", bpf_fd, + server_pid); + return -1; + } + int res = bpftime_attach_perf_to_bpf(perf_id, bpf_id); + if (res < 0) { + spdlog::error("Failed to attach perf to bpf"); + return -1; + } + spdlog::info("attach perf {} to bpf {}, for pid {}", perf_id, bpf_id, server_pid); + return 0; +} + +int bpftime_driver::bpftime_uprobe_create_server(int server_pid, int fd, + int target_pid, + const char *name, + uint64_t offset, bool retprobe, + size_t ref_ctr_off) +{ + int id = find_minimal_unused_id(); + int res = bpftime_uprobe_create(id, target_pid, name, offset, retprobe, + ref_ctr_off); + if (res < 0) { + spdlog::error("Failed to create uprobe"); + return -1; + } + pid_fd_to_id_map[get_pid_fd_key(server_pid, fd)] = id; + spdlog::info("create uprobe {} for pid {} fd {}", id, server_pid, fd); + return id; +} + +// enable the perf event +int bpftime_driver::bpftime_perf_event_enable_server(int server_pid, int fd) +{ + int fd_id = check_and_get_pid_fd(server_pid, fd); + if (fd_id < 0) { + spdlog::error("fd {} for pid {} not exists", fd, server_pid); + return -1; + } + int res = bpftime_perf_event_enable(fd_id); + if (res < 0) { + spdlog::error("Failed to enable perf event"); + return -1; + } + spdlog::info("enable perf event {} for pid {} fd {}", fd_id, server_pid, fd); + return 0; +} +// disable the perf event +int bpftime_driver::bpftime_perf_event_disable_server(int server_pid, int fd) +{ + int fd_id = check_and_get_pid_fd(server_pid, fd); + if (fd_id < 0) { + spdlog::error("fd {} for pid {} not exists", fd, server_pid); + return -1; + } + int res = bpftime_perf_event_disable(fd_id); + if (res < 0) { + spdlog::error("Failed to disable perf event"); + return -1; + } + spdlog::info("disable perf event {} for pid {} fd {}", fd_id, server_pid, fd); + return 0; +} + +void bpftime_driver::bpftime_close_server(int server_pid, int fd) +{ + int fd_id = check_and_get_pid_fd(server_pid, fd); + if (fd_id < 0) { + spdlog::error("fd {} for pid {} not exists", fd, server_pid); + return; + } + pid_fd_to_id_map.erase(get_pid_fd_key(server_pid, fd)); + bpftime_close(fd_id); + spdlog::info("close id {} for pid {} fd {}", fd_id, server_pid, fd); +} + +bpftime_driver::bpftime_driver(daemon_config cfg) +{ + config = cfg; + bpftime_initialize_global_shm(shm_open_type::SHM_REMOVE_AND_CREATE); +} + +bpftime_driver::~bpftime_driver() +{ + bpftime_destroy_global_shm(); +} \ No newline at end of file diff --git a/daemon/user/bpftime_driver.hpp b/daemon/user/bpftime_driver.hpp new file mode 100644 index 00000000..e9818742 --- /dev/null +++ b/daemon/user/bpftime_driver.hpp @@ -0,0 +1,82 @@ +#ifndef BPFTIME_DRIVER_HPP +#define BPFTIME_DRIVER_HPP + +#include "daemon_config.hpp" +#include "bpftime_shm.hpp" +#include +#include +#include + +namespace bpftime +{ + +// Use commands to interact with the bpftime agent and shm maps +class bpftime_driver { + int find_minimal_unused_id(); + inline uint64_t get_pid_fd_key(int pid, int fd) + { + return ((uint64_t)pid << 32) | (uint64_t)fd; + } + inline int check_and_get_pid_fd(int pid, int fd) + { + auto it = pid_fd_to_id_map.find(get_pid_fd_key(pid, fd)); + if (it == pid_fd_to_id_map.end()) { + return -1; + } + return it->second; + } + std::map pid_fd_to_id_map; + daemon_config config; + + public: + // create a bpf link in the global shared memory + // + // @param[fd]: fd is the fd allocated by the kernel. if fd is -1, then + // the function will allocate a new perf event fd. + int bpftime_link_create_server(int server_pid, int fd, int prog_fd, + int target_fd); + + // create a bpf prog in the global shared memory + // + // @param[fd]: fd is the fd allocated by the kernel. if fd is -1, then + // the function will allocate a new perf event fd. + int bpftime_progs_create_server(int server_pid, int fd, const ebpf_inst *insn, + size_t insn_cnt, const char *prog_name, + int prog_type); + + // create a bpf map in the global shared memory + // + // @param[fd]: fd is the fd allocated by the kernel. if fd is -1, then + // the function will allocate a new perf event fd. + int bpftime_maps_create_server(int server_pid, int fd, const char *name, + bpftime::bpf_map_attr attr); + + int bpftime_attach_perf_to_bpf_server(int server_pid, int perf_fd, int bpf_fd); + + // create uprobe in the global shared memory + // + // @param[fd]: fd is the fd allocated by the kernel. if fd is -1, then + // the function will allocate a new perf event fd. + int bpftime_uprobe_create_server(int server_pid, int fd, int target_pid, + const char *name, uint64_t offset, + bool retprobe, size_t ref_ctr_off); + // create tracepoint in the global shared memory + // + // @param[fd]: fd is the fd allocated by the kernel. if fd is -1, then + // the function will allocate a new perf event fd. + int bpftime_tracepoint_create_server(int server_pid, int fd, int pid, + int32_t tp_id); + // enable the perf event + int bpftime_perf_event_enable_server(int server_pid, int fd); + // disable the perf event + int bpftime_perf_event_disable_server(int server_pid, int fd); + + void bpftime_close_server(int server_pid, int fd); + + bpftime_driver(struct daemon_config cfg); + ~bpftime_driver(); +}; + +} // namespace bpftime + +#endif // BPFTIME_DRIVER_HPP diff --git a/daemon/daemon.hpp b/daemon/user/daemon.hpp similarity index 78% rename from daemon/daemon.hpp rename to daemon/user/daemon.hpp index ac18146b..b1dce4f0 100644 --- a/daemon/daemon.hpp +++ b/daemon/user/daemon.hpp @@ -5,7 +5,7 @@ namespace bpftime { -int start_daemon(struct env env); +int start_daemon(struct daemon_config env); } // namespace bpftime #endif // BPFTIME_DAEMON_HPP \ No newline at end of file diff --git a/daemon/user/daemon_config.hpp b/daemon/user/daemon_config.hpp new file mode 100644 index 00000000..8d12d1d7 --- /dev/null +++ b/daemon/user/daemon_config.hpp @@ -0,0 +1,33 @@ +#ifndef BPFTIME_DAEMON_CONFIG_HPP +#define BPFTIME_DAEMON_CONFIG_HPP + +#include +#include + +#define PATH_LENTH 255 + +// configuration for bpftime daemon +struct daemon_config { + // the target pid of eBPF application to trace + pid_t pid = 0; + // the target uid of eBPF application to trace + uid_t uid = 0; + // print verbose debug output + bool verbose = false; + // print open syscalls (default: false) + // Open syscall may related to bpf config, so we need to handle it + bool show_open = false; + // enable replace prog to support bypass kernel verifier + bool enable_replace_prog = false; + // enable replace uprobe to make kernel uprobe not break user space uprobe + bool enable_replace_uprobe = true; + char new_uprobe_path[PATH_LENTH] = "\0"; + // bpftime cli path for bpftime daemon to create prog and link, maps + std::string bpftime_cli_path = "~/.bpftime/bpftime"; + // bpftime tool path for bpftime daemon to run bpftime + std::string bpftime_tool_path = "~/.bpftime/bpftimetool"; + // should bpftime be involve + bool is_driving_bpftime = true; +}; + +#endif // BPFTIME_DAEMON_CONFIG_HPP diff --git a/daemon/handle_bpf_event.cpp b/daemon/user/handle_bpf_event.cpp similarity index 59% rename from daemon/handle_bpf_event.cpp rename to daemon/user/handle_bpf_event.cpp index 6568e814..156878f4 100644 --- a/daemon/handle_bpf_event.cpp +++ b/daemon/user/handle_bpf_event.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -7,7 +8,10 @@ #include #include #include "handle_bpf_event.hpp" -#include "bpf-mocker-event.h" +#include "../bpf_tracer_event.h" + +#define PERF_UPROBE_REF_CTR_OFFSET_BITS 32 +#define PERF_UPROBE_REF_CTR_OFFSET_SHIFT 32 using namespace bpftime; @@ -52,25 +56,23 @@ int bpftime::determine_uprobe_perf_type(void) return parse_uint_from_file(file, "%d\n"); } +int bpftime::determine_uprobe_retprobe_bit() +{ + const char *file = + "/sys/bus/event_source/devices/uprobe/format/retprobe"; + + return parse_uint_from_file(file, "config:%d\n"); +} + int bpf_event_handler::handle_open_events(const struct event *e) { struct tm *tm; char ts[32]; time_t t; - int fd, err; if (!config.show_open) { return 0; } - /* prepare fields */ - if (e->open_data.ret >= 0) { - fd = e->open_data.ret; - err = 0; - } else { - fd = -1; - err = -e->open_data.ret; - } - /* print output */ spdlog::info("OPEN {:<6} {:<16}", e->pid, e->comm); return 0; @@ -180,8 +182,13 @@ static const char *get_bpf_map_type_string(enum bpf_map_type type) return "Unknown"; } -int bpf_event_handler::handle_close_event(const struct event *e) { - spdlog::info("CLOSE {:<6} {:<16} fd:{}", e->pid, e->comm, e->close_data.fd); +int bpf_event_handler::handle_close_event(const struct event *e) +{ + spdlog::info("CLOSE {:<6} {:<16} fd:{}", e->pid, e->comm, + e->close_data.fd); + if (config.is_driving_bpftime) { + driver.bpftime_close_server(e->pid, e->close_data.fd); + } return 0; } @@ -205,12 +212,55 @@ int bpf_event_handler::handle_bpf_event(const struct event *e) get_bpf_map_type_string( (enum bpf_map_type)e->bpf_data.attr.map_type), e->bpf_data.attr.map_name); + if (config.is_driving_bpftime && e->bpf_data.ret > 0) { + bpftime::bpf_map_attr attr; + attr.type = e->bpf_data.attr.map_type; + attr.key_size = e->bpf_data.attr.key_size; + attr.value_size = e->bpf_data.attr.value_size; + attr.max_ents = e->bpf_data.attr.max_entries; + attr.flags = e->bpf_data.attr.map_flags; + attr.btf_id = e->bpf_data.attr.btf_fd; + attr.btf_key_type_id = e->bpf_data.attr.btf_key_type_id; + attr.btf_value_type_id = + e->bpf_data.attr.btf_value_type_id; + attr.btf_vmlinux_value_type_id = + e->bpf_data.attr.btf_vmlinux_value_type_id; + // attr.map_extra = e->bpf_data.attr.map_extra; + attr.ifindex = e->bpf_data.attr.map_ifindex; + driver.bpftime_maps_create_server( + e->pid, e->bpf_data.ret, + e->bpf_data.attr.map_name, attr); + } break; case BPF_LINK_CREATE: /* code */ spdlog::info(" BPF_LINK_CREATE prog_fd:{} target_fd:{}", e->bpf_data.attr.link_create.prog_fd, e->bpf_data.attr.link_create.target_fd); + if (config.is_driving_bpftime && e->bpf_data.ret > 0) { + return driver.bpftime_link_create_server( + e->pid, e->bpf_data.ret, + e->bpf_data.attr.link_create.prog_fd, + e->bpf_data.attr.link_create.target_fd); + } + break; + case BPF_PROG_LOAD: + /* code */ + spdlog::info( + " BPF_PROG_LOAD prog_type:{:<16} prog_name:{:<16}", + bpf_prog_type_strings[e->bpf_data.attr.prog_type], + e->bpf_data.attr.prog_name); + if (config.is_driving_bpftime && e->bpf_data.ret > 0) { + event load_prog_event = + bpf_prog_map[e->bpf_data.attr.insns]; + return driver.bpftime_progs_create_server( + e->pid, e->bpf_data.ret, + (ebpf_inst *) + load_prog_event.bpf_loaded_prog.insns, + load_prog_event.bpf_loaded_prog.insn_cnt, + e->bpf_data.attr.prog_name, + e->bpf_data.attr.prog_type); + } break; default: break; @@ -221,26 +271,42 @@ int bpf_event_handler::handle_bpf_event(const struct event *e) #define PERF_TYPE_MAX_ID 16 static const char *perf_type_id_strings[PERF_TYPE_MAX_ID] = { - "PERF_TYPE_HARDWARE", - "PERF_TYPE_SOFTWARE", - "PERF_TYPE_TRACEPOINT", - "PERF_TYPE_HW_CACHE", - "PERF_TYPE_RAW", - "PERF_TYPE_BREAKPOINT", + "PERF_TYPE_HARDWARE", "PERF_TYPE_SOFTWARE", "PERF_TYPE_TRACEPOINT", + "PERF_TYPE_HW_CACHE", "PERF_TYPE_RAW", "PERF_TYPE_BREAKPOINT", }; int bpf_event_handler::handle_perf_event(const struct event *e) { - const char *type_id_str = - e->perf_event_data.attr.type >= - (sizeof(perf_type_id_strings) / - sizeof(perf_type_id_strings[0])) ? - "UNKNOWN TYPE" : - perf_type_id_strings[e->perf_event_data.attr.type]; + const char *type_id_str = "UNKNOWN TYPE"; + unsigned int perf_type = e->perf_event_data.attr.type; + if (perf_type >= 0 && perf_type < (sizeof(perf_type_id_strings) / + sizeof(perf_type_id_strings[0]))) { + type_id_str = perf_type_id_strings[perf_type]; + } /* print output */ - spdlog::info("PERF {:<6} {:<16} type:{:<16} ret:{}\n", e->pid, e->comm, - type_id_str, e->perf_event_data.ret); + spdlog::info("PERF {:<6} {:<16} type:{:<16} ret:{}\n", e->pid, + e->comm, type_id_str, e->perf_event_data.ret); + if (config.is_driving_bpftime && e->perf_event_data.ret > 0) { + if (perf_type == (unsigned int)uprobe_type) { + auto attr = &e->perf_event_data.attr; + // NO legacy bpf types + bool retprobe = attr->config & + (1 << determine_uprobe_retprobe_bit()); + spdlog::debug("retprobe {}", retprobe); + size_t ref_ctr_off = attr->config >> + PERF_UPROBE_REF_CTR_OFFSET_SHIFT; + const char *name = e->perf_event_data.name_or_path; + uint64_t offset = e->perf_event_data.attr.probe_offset; + spdlog::debug("Creating uprobe name {} offset {} " + "ref_ctr_off {} attr->config={:x}", + name, offset, ref_ctr_off, attr->config); + driver.bpftime_uprobe_create_server( + e->pid, e->perf_event_data.ret, + e->perf_event_data.pid, name, offset, retprobe, + ref_ctr_off); + } + } return 0; } @@ -252,18 +318,57 @@ int bpf_event_handler::handle_load_bpf_prog_event(const struct event *e) "UNKNOWN PROG TYPE" : bpf_prog_type_strings[e->bpf_loaded_prog.type]; - const char* prog_name = strlen(e->bpf_loaded_prog.prog_name) > 0 ? e->bpf_loaded_prog.prog_name : "(none)"; + const char *prog_name = strlen(e->bpf_loaded_prog.prog_name) > 0 ? + e->bpf_loaded_prog.prog_name : + "(none)"; /* print output */ spdlog::info( "BPF_LOAD {:<6} {:<16} name:{:<16} type:{:<16} insn_cnt:{:<6}", e->pid, e->comm, prog_name, prog_type_str, e->bpf_loaded_prog.insn_cnt); + // save the program in the map for later lookup in prog load event + bpf_prog_map[e->bpf_loaded_prog.insns_ptr] = *e; + return 0; +} + +int bpf_event_handler::handle_ioctl(const struct event *e) +{ + int res; + int fd = e->ioctl_data.fd; + int req = e->ioctl_data.req; + int data = e->ioctl_data.data; + spdlog::info("IOCTL {:<6} {:<16} fd:{} req:{} data:{}", e->pid, + e->comm, fd, req, data); + if (req == PERF_EVENT_IOC_ENABLE) { + spdlog::info("Enabling perf event {}", fd); + if (config.is_driving_bpftime) { + return driver.bpftime_perf_event_enable_server(e->pid, + fd); + } + } else if (req == PERF_EVENT_IOC_DISABLE) { + spdlog::info("Disabling perf event {}", fd); + if (config.is_driving_bpftime) { + return driver.bpftime_perf_event_disable_server(e->pid, + fd); + } + } else if (req == PERF_EVENT_IOC_SET_BPF) { + spdlog::info("Setting bpf for perf event {} and bpf {}", fd, + data); + if (config.is_driving_bpftime) { + return driver.bpftime_attach_perf_to_bpf_server( + e->pid, fd, data); + } + } return 0; } int bpf_event_handler::handle_event(const struct event *e) { + // ignore events from self + if (e->pid == current_pid) { + return 0; + } switch (e->type) { case SYS_OPEN: return handle_open_events(e); @@ -280,22 +385,29 @@ int bpf_event_handler::handle_event(const struct event *e) case SYS_CLOSE: return handle_close_event(e); break; + case SYS_IOCTL: + return handle_ioctl(e); + break; } return 0; } -bpf_event_handler::bpf_event_handler(struct env config) : config(config) +bpf_event_handler::bpf_event_handler(struct daemon_config config, + bpftime_driver &driver) + : config(config), driver(driver) { - int uprobe_type = determine_uprobe_perf_type(); + current_pid = getpid(); + uprobe_type = determine_uprobe_perf_type(); if (uprobe_type < 0 || uprobe_type >= PERF_TYPE_MAX_ID) { spdlog::error("Failed to determine uprobe perf type"); exit(1); } perf_type_id_strings[uprobe_type] = "PERF_TYPE_UPROBE"; - int kprobe_type = determine_kprobe_perf_type(); + kprobe_type = determine_kprobe_perf_type(); if (kprobe_type < 0 || kprobe_type >= PERF_TYPE_MAX_ID) { spdlog::error("Failed to determine kprobe perf type"); exit(1); } + perf_type_id_strings[kprobe_type] = "PERF_TYPE_KPROBE"; } \ No newline at end of file diff --git a/daemon/handle_bpf_event.hpp b/daemon/user/handle_bpf_event.hpp similarity index 50% rename from daemon/handle_bpf_event.hpp rename to daemon/user/handle_bpf_event.hpp index a241543d..2c22a983 100644 --- a/daemon/handle_bpf_event.hpp +++ b/daemon/user/handle_bpf_event.hpp @@ -1,29 +1,44 @@ #ifndef BPFTIME_HANDLE_EVENT_HPP #define BPFTIME_HANDLE_EVENT_HPP -#include "bpf-mocker-event.h" +#include "../bpf_tracer_event.h" #include "daemon_config.hpp" +#include "bpftime_driver.hpp" +#include +#include namespace bpftime { class bpf_event_handler { - struct env config; + std::map bpf_prog_map; + int current_pid = 0; + int uprobe_type = -1; + int kprobe_type = -1; + + struct daemon_config config; + bpftime_driver &driver; int handle_close_event(const struct event *e); int handle_bpf_event(const struct event *e); int handle_open_events(const struct event *e); int handle_perf_event(const struct event *e); int handle_load_bpf_prog_event(const struct event *e); + int handle_ioctl(const struct event *e); public: + // callback function for bpf events in ring buffer int handle_event(const struct event *e); - bpf_event_handler(struct env config); + bpf_event_handler(struct daemon_config config, bpftime_driver &driver); }; +// determine the perf type for kprobe, exit if failed int determine_kprobe_perf_type(void); +// determine the perf type for uprobe, exit if failed int determine_uprobe_perf_type(void); +int determine_uprobe_retprobe_bit(); + } // namespace bpftime #endif // BPFTIME_HANDLE_EVENT_HPP diff --git a/daemon/main.cpp b/daemon/user/main.cpp similarity index 91% rename from daemon/main.cpp rename to daemon/user/main.cpp index 6c715a62..5ea920e8 100644 --- a/daemon/main.cpp +++ b/daemon/user/main.cpp @@ -8,7 +8,7 @@ using namespace bpftime; -static struct env env = { .uid = static_cast(-1) }; +static struct daemon_config env = { .uid = static_cast(-1) }; const char *argp_program_version = "bpftime-daemon 0.1"; const char *argp_program_bug_address = "https://github.com/eunomia-bpf/bpftime"; @@ -19,7 +19,6 @@ static const struct argp_option opts[] = { { "uid", 'u', "UID", 0, "User ID to trace" }, { "open", 'o', "OPEN", 0, "Show open events" }, { "verbose", 'v', NULL, 0, "Verbose debug output" }, - { "failed", 'x', NULL, 0, "Failed opens only" }, {}, }; @@ -32,9 +31,6 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) case 'v': env.verbose = true; break; - case 'x': - env.failed = true; - break; case 'o': env.show_open = true; break; @@ -78,7 +74,8 @@ int main(int argc, char **argv) .doc = argp_program_doc, }; int err; - + // use current path as default path + strncpy(env.new_uprobe_path, argv[0], PATH_LENTH); err = argp_parse(&argp, argc, argv, 0, NULL, NULL); if (err) return err; diff --git a/example/minimal/.gitignore b/example/minimal/.gitignore new file mode 100644 index 00000000..913c8c36 --- /dev/null +++ b/example/minimal/.gitignore @@ -0,0 +1,11 @@ +.vscode +package.json +*.o +*.skel.json +*.skel.yaml +package.yaml +ecli +minimal +.output +test +victim diff --git a/example/minimal/Makefile b/example/minimal/Makefile new file mode 100644 index 00000000..38fc809f --- /dev/null +++ b/example/minimal/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 +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 = minimal # 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) victim + +victim: victim.c + gcc victim.c -o victim -Wall -g + +.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/example/minimal/README.md b/example/minimal/README.md new file mode 100644 index 00000000..fe2529d2 --- /dev/null +++ b/example/minimal/README.md @@ -0,0 +1,70 @@ +# minimal trace + +This code is a BPF (Berkeley Packet Filter) program written in C, often used for tracing and monitoring activities in the Linux kernel. BPF allows you to run custom programs within the kernel without modifying its source code. The code you provided creates a BPF program that uses a BPF map to count the number of times the `minimal` function is called within a specified cgroup. + +```c +#include +#include +#include "bits.bpf.h" +#include "maps.bpf.h" + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, u64); + __type(value, u64); +} libc_minimal_calls_total SEC(".maps"); + +SEC("uprobe/libc.so.6:minimal") +int do_count(struct pt_regs *ctx) +{ + u64 cgroup_id = bpf_get_current_cgroup_id(); + + increment_map(&libc_minimal_calls_total, &cgroup_id, 1); + + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; +``` + +from + +Here's a breakdown of the code: + +1. **Headers Inclusion**: + - ``: Provides access to kernel data structures and definitions. + - ``: Includes helper functions and macros for BPF programs. + - `"bits.bpf.h"`: Custom header file (assumed to contain additional definitions). + - `"maps.bpf.h"`: Custom header file (assumed to contain definitions related to BPF maps). + +2. **Definition of BPF Map**: + The code defines a BPF map named `libc_minimal_calls_total` using the `struct` syntax. This map is of type `BPF_MAP_TYPE_HASH` (hash map) with a maximum of 1024 entries. The keys and values are of type `u64` (unsigned 64-bit integer). + +3. **Map Definition Attributes**: + The attributes specified within the map definition (`__uint`, `__type`) set properties of the map, such as its type, maximum number of entries, and types of keys and values. + +4. **BPF Program**: + - The program is associated with a `uprobe` on the `minimal` function in the `libc.so.6` library. + - The `do_count` function is executed when the `minimal` function is called. + - It retrieves the current cgroup ID using `bpf_get_current_cgroup_id()`. + - Then, it increments the `libc_minimal_calls_total` map with the cgroup ID as the key and increments the associated value by 1. + +5. **License Information**: + The `LICENSE[]` array contains the license information for the BPF program. In this case, the program is licensed under the GPL (GNU General Public License). + +The purpose of this BPF program is to track and count the number of `minimal` calls that occur within specific cgroups in the Linux kernel. It uses a BPF hash map to store and update the counts. This can be useful for monitoring memory allocation patterns and resource usage within different cgroups. + +## how to run + +server + +```sh +LD_PRELOAD=build/runtime/syscall-server/libbpftime-syscall-server.so example/minimal/minimal +``` + +client + +```sh +LD_PRELOAD=build/runtime/agent/libbpftime-agent.so example/minimal/test +``` diff --git a/example/minimal/minimal.bpf.c b/example/minimal/minimal.bpf.c new file mode 100644 index 00000000..8b3f5226 --- /dev/null +++ b/example/minimal/minimal.bpf.c @@ -0,0 +1,13 @@ +#define BPF_NO_GLOBAL_DATA +#include +#include +#include + +SEC("uprobe/example/minimal/victim:target_func") +int do_uprobe_trace(struct pt_regs *ctx) +{ + bpf_printk("target_func called.\n"); + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/example/minimal/minimal.c b/example/minimal/minimal.c new file mode 100644 index 00000000..abce26ea --- /dev/null +++ b/example/minimal/minimal.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2020 Facebook */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "minimal.skel.h" +#include + +#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 minimal_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 = minimal_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + /* Load & verify BPF programs */ + err = minimal_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + LIBBPF_OPTS(bpf_uprobe_opts, attach_opts, .func_name = "target_func", + .retprobe = false, .attach_mode = PROBE_ATTACH_MODE_PERF); + struct bpf_link *attach = bpf_program__attach_uprobe_opts( + skel->progs.do_uprobe_trace, -1, "example/minimal/victim", 0, + &attach_opts); + if (!attach) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + err = -1; + goto cleanup; + } + while (!exiting) { + sleep(1); + } +cleanup: + /* Clean up */ + minimal_bpf__destroy(skel); + + return err < 0 ? -err : 0; +} diff --git a/example/minimal/victim.c b/example/minimal/victim.c new file mode 100644 index 00000000..ec323e1e --- /dev/null +++ b/example/minimal/victim.c @@ -0,0 +1,16 @@ +#include +#include +#include + +int target_func() { + printf("target_func\n"); + return 0; +} + +int main(int argc, char *argv[]) { + while(1) { + sleep(2); + target_func(); + } + return 0; +} diff --git a/runtime/agent/agent.cpp b/runtime/agent/agent.cpp index 2f66bdc6..ce828209 100644 --- a/runtime/agent/agent.cpp +++ b/runtime/agent/agent.cpp @@ -20,8 +20,6 @@ #include using namespace bpftime; -const shm_open_type bpftime::global_shm_open_type = shm_open_type::SHM_CLIENT; - using main_func_t = int (*)(int, char **, char **); static main_func_t orig_main_func = nullptr; @@ -76,7 +74,7 @@ extern "C" int __libc_start_main(int (*main)(int, char **, char **), int argc, extern "C" void bpftime_agent_main(const gchar *data, gboolean *stay_resident) { - bpftime_initialize_global_shm(); + bpftime_initialize_global_shm(shm_open_type::SHM_OPEN_ONLY); ctx_holder.init(); ctx_holder.ctx.set_orig_syscall_func(orig_hooker); spdlog::cfg::load_env_levels(); diff --git a/runtime/include/bpftime_shm.hpp b/runtime/include/bpftime_shm.hpp index ca73626f..12eeb5af 100644 --- a/runtime/include/bpftime_shm.hpp +++ b/runtime/include/bpftime_shm.hpp @@ -30,9 +30,10 @@ struct bpf_map_attr { }; enum class shm_open_type { - SHM_SERVER, - SHM_CLIENT, + SHM_REMOVE_AND_CREATE, + SHM_OPEN_ONLY, SHM_NO_CREATE, + SHM_CREATE_OR_OPEN, }; extern const shm_open_type global_shm_open_type; @@ -43,9 +44,14 @@ bpftime::agent_config &bpftime_get_agent_config(); extern "C" { // initialize the global shared memory for store bpf progs and maps -void bpftime_initialize_global_shm(); -// destroy the global shared memory +void bpftime_initialize_global_shm(bpftime::shm_open_type type); +// destroy the global shared memory data structure +// +// Note: this will NO remove the global shared memory from system +// use bpftime_remove_global_shm() to remove the global shared memory void bpftime_destroy_global_shm(); +// remove the global shared memory from system +void bpftime_remove_global_shm(); // import the global shared memory from json file int bpftime_import_global_shm_from_json(const char *filename); diff --git a/runtime/src/bpf_helper.cpp b/runtime/src/bpf_helper.cpp index 06967ef9..506e4485 100644 --- a/runtime/src/bpf_helper.cpp +++ b/runtime/src/bpf_helper.cpp @@ -148,6 +148,7 @@ uint64_t bpf_get_stack(uint64_t, uint64_t buf, uint64_t sz, uint64_t, uint64_t) memset((void *)(uintptr_t)buf, 0, sz); return sz; } + uint64_t bpf_ktime_get_coarse_ns(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) { @@ -155,6 +156,7 @@ uint64_t bpf_ktime_get_coarse_ns(uint64_t, uint64_t, uint64_t, uint64_t, clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); return (uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec; } + uint64_t bpf_ringbuf_output(uint64_t rb, uint64_t data, uint64_t size, uint64_t flags, uint64_t) { @@ -184,6 +186,7 @@ uint64_t bpf_ringbuf_reserve(uint64_t rb, uint64_t size, uint64_t flags, } return (uint64_t)(uintptr_t)bpftime_ringbuf_reserve(fd, size); } + uint64_t bpf_ringbuf_submit(uint64_t data, uint64_t flags, uint64_t, uint64_t, uint64_t) { @@ -208,6 +211,7 @@ uint64_t bpf_ringbuf_discard(uint64_t data, uint64_t flags, uint64_t, uint64_t, bpftime_ringbuf_submit(fd, (void *)(uintptr_t)data, true); return 0; } + uint64_t bpf_perf_event_output(uint64_t ctx, uint64_t map, uint64_t flags, uint64_t data, uint64_t size) { diff --git a/runtime/src/bpftime_shm_internal.cpp b/runtime/src/bpftime_shm_internal.cpp index 865974c4..e27853ea 100644 --- a/runtime/src/bpftime_shm_internal.cpp +++ b/runtime/src/bpftime_shm_internal.cpp @@ -10,16 +10,16 @@ static bool global_shm_initialized = false; -void bpftime_initialize_global_shm() +extern "C" void bpftime_initialize_global_shm(bpftime::shm_open_type type) { using namespace bpftime; // Use placement new, which will not allocate memory, but just // call the constructor - new (&shm_holder.global_shared_memory) bpftime_shm; + new (&shm_holder.global_shared_memory) bpftime_shm(type); global_shm_initialized = true; } -void bpftime_destroy_global_shm() +extern "C" void bpftime_destroy_global_shm() { using namespace bpftime; if (global_shm_initialized) { @@ -30,10 +30,20 @@ void bpftime_destroy_global_shm() printf("INFO [%d]: Global shm destructed\n", (int)getpid()); } } + +extern "C" void bpftime_remove_global_shm() +{ + using namespace bpftime; + boost::interprocess::shared_memory_object::remove( + get_global_shm_name()); + spdlog::info("Global shm removed"); +} + static __attribute__((destructor(65535))) void __destruct_shm() { bpftime_destroy_global_shm(); } + namespace bpftime { @@ -124,12 +134,11 @@ int bpftime_shm::add_uprobe(int fd, int pid, const char *name, uint64_t offset, // if fd is negative, we need to create a new fd for allocating fd = open_fake_fd(); } - manager->set_handler( + return manager->set_handler( fd, bpftime::bpf_perf_event_handler{ retprobe, offset, pid, name, ref_ctr_off, segment }, segment); - return fd; } int bpftime_shm::add_tracepoint(int fd, int pid, int32_t tracepoint_id) @@ -138,26 +147,37 @@ int bpftime_shm::add_tracepoint(int fd, int pid, int32_t tracepoint_id) // if fd is negative, we need to create a new fd for allocating fd = open_fake_fd(); } - manager->set_handler(fd, - bpftime::bpf_perf_event_handler(pid, tracepoint_id, - segment), - segment); - return fd; + return manager->set_handler( + fd, + bpftime::bpf_perf_event_handler(pid, tracepoint_id, segment), + segment); } int bpftime_shm::add_software_perf_event(int cpu, int32_t sample_type, int64_t config) { int fd = open_fake_fd(); - manager->set_handler(fd, - bpftime::bpf_perf_event_handler(cpu, sample_type, - config, segment), - segment); + return manager->set_handler(fd, + bpftime::bpf_perf_event_handler( + cpu, sample_type, config, segment), + segment); return fd; } + int bpftime_shm::attach_perf_to_bpf(int perf_fd, int bpf_fd) { - if (!is_perf_fd(perf_fd) || !is_prog_fd(bpf_fd)) { + if (!is_perf_fd(perf_fd)) { + spdlog::error("Fd {} not a perf fd", perf_fd); + errno = ENOENT; + return -1; + } + return add_bpf_prog_attach_target(perf_fd, bpf_fd); +} + +int bpftime_shm::add_bpf_prog_attach_target(int perf_fd, int bpf_fd) +{ + if (!is_prog_fd(bpf_fd)) { + spdlog::error("Fd {} not prog fd", bpf_fd); errno = ENOENT; return -1; } @@ -270,7 +290,7 @@ int bpftime_shm::epoll_create() fd); return -1; } - manager->set_handler(fd, bpftime::epoll_handler(segment), segment); + fd = manager->set_handler(fd, bpftime::epoll_handler(segment), segment); spdlog::debug("Epoll instance created: fd={}", fd); return fd; } @@ -367,11 +387,11 @@ int bpftime_shm::add_bpf_prog(int fd, const ebpf_inst *insn, size_t insn_cnt, // if fd is negative, we need to create a new fd for allocating fd = open_fake_fd(); } - manager->set_handler(fd, - bpftime::bpf_prog_handler(segment, insn, insn_cnt, - prog_name, prog_type), - segment); - return fd; + return manager->set_handler( + fd, + bpftime::bpf_prog_handler(segment, insn, insn_cnt, prog_name, + prog_type), + segment); } // add a bpf link fd @@ -384,11 +404,11 @@ int bpftime_shm::add_bpf_link(int fd, int prog_fd, int target_fd) if (!manager->is_allocated(target_fd) || !is_prog_fd(prog_fd)) { return -1; } - manager->set_handler(fd, - bpftime::bpf_link_handler{ (uint32_t)prog_fd, - (uint32_t)target_fd }, - segment); - return fd; + return manager->set_handler( + fd, + bpftime::bpf_link_handler{ (uint32_t)prog_fd, + (uint32_t)target_fd }, + segment); } void bpftime_shm::close_fd(int fd) @@ -407,14 +427,13 @@ bool bpftime_shm::is_exist_fake_fd(int fd) const return manager->is_allocated(fd); } -bpftime_shm::bpftime_shm(const char* shm_name, shm_open_type type) +bpftime_shm::bpftime_shm(const char *shm_name, shm_open_type type) { - if (type == shm_open_type::SHM_CLIENT) { + if (type == shm_open_type::SHM_OPEN_ONLY) { spdlog::debug("start: bpftime_shm for client setup"); // open the shm segment = boost::interprocess::managed_shared_memory( - boost::interprocess::open_only, - shm_name); + boost::interprocess::open_only, shm_name); manager = segment.find( bpftime::DEFAULT_GLOBAL_HANDLER_NAME) .first; @@ -427,10 +446,32 @@ bpftime_shm::bpftime_shm(const char* shm_name, shm_open_type type) bpftime::DEFAULT_AGENT_CONFIG_NAME) .first; spdlog::debug("done: bpftime_shm for client setup"); - } else if (type == shm_open_type::SHM_SERVER) { + } else if (type == shm_open_type::SHM_CREATE_OR_OPEN) { + spdlog::debug("start: bpftime_shm for create or open setup"); + segment = boost::interprocess::managed_shared_memory( + boost::interprocess::open_or_create, + // Allocate 20M bytes of memory by default + shm_name, 20 << 20); + + manager = segment.find_or_construct( + bpftime::DEFAULT_GLOBAL_HANDLER_NAME)(segment); + spdlog::debug("done: bpftime_shm for server setup: manager"); + + syscall_installed_pids = + segment.find_or_construct( + bpftime::DEFAULT_SYSCALL_PID_SET_NAME)( + std::less(), + syscall_pid_set_allocator( + segment.get_segment_manager())); + spdlog::debug( + "done: bpftime_shm for server setup: syscall_pid_set"); + + agent_config = segment.find_or_construct( + bpftime::DEFAULT_AGENT_CONFIG_NAME)(); + spdlog::debug("done: bpftime_shm for open_or_create setup"); + } else if (type == shm_open_type::SHM_REMOVE_AND_CREATE) { spdlog::debug("start: bpftime_shm for server setup"); - boost::interprocess::shared_memory_object::remove( - shm_name); + boost::interprocess::shared_memory_object::remove(shm_name); // create the shm spdlog::debug( "done: bpftime_shm for server setup: remove installed segment"); @@ -460,16 +501,16 @@ bpftime_shm::bpftime_shm(const char* shm_name, shm_open_type type) } else if (type == shm_open_type::SHM_NO_CREATE) { // not create any shm spdlog::warn( - "NOT creating global shm. Please check if you declared bpftime::global_shm_open_type"); + "NOT creating global shm. This is only for testing purpose."); return; } } -bpftime_shm::bpftime_shm() - : bpftime_shm(bpftime::get_global_shm_name(), global_shm_open_type) +bpftime_shm::bpftime_shm(bpftime::shm_open_type type) + : bpftime_shm(bpftime::get_global_shm_name(), type) { - spdlog::info("Global shm constructed. global_shm_open_type {} for {}", - (int)global_shm_open_type, bpftime::get_global_shm_name()); + spdlog::info("Global shm constructed. shm_open_type {} for {}", + (int)type, bpftime::get_global_shm_name()); } int bpftime_shm::add_bpf_map(int fd, const char *name, @@ -494,9 +535,8 @@ int bpftime_shm::add_bpf_map(int fd, const char *name, }; verifier::set_map_descriptors(helpers); #endif - manager->set_handler(fd, bpftime::bpf_map_handler(name, segment, attr), - segment); - return fd; + return manager->set_handler( + fd, bpftime::bpf_map_handler(name, segment, attr), segment); } const handler_manager *bpftime_shm::get_manager() const @@ -535,8 +575,4 @@ bpftime_shm::get_software_perf_event_raw_buffer(int fd, size_t buffer_sz) const return handler.try_get_software_perf_data_raw_buffer(buffer_sz); } -// Declare it as weak symbol, which could be overrided by other symbols -extern const __attribute__((weak)) bpftime::shm_open_type global_shm_open_type = - bpftime::shm_open_type::SHM_NO_CREATE; - } // namespace bpftime diff --git a/runtime/src/bpftime_shm_internal.hpp b/runtime/src/bpftime_shm_internal.hpp index 4a2aa2c9..b2c4632f 100644 --- a/runtime/src/bpftime_shm_internal.hpp +++ b/runtime/src/bpftime_shm_internal.hpp @@ -77,7 +77,7 @@ class bpftime_shm { bool from_userspace) const; long bpf_map_update_elem(int fd, const void *key, const void *value, - uint64_t flags, bool from_userspace) const; + uint64_t flags, bool from_userspace) const; long bpf_delete_elem(int fd, const void *key, bool from_userspace) const; @@ -88,11 +88,21 @@ class bpftime_shm { // create an uprobe fd int add_uprobe(int fd, int pid, const char *name, uint64_t offset, bool retprobe, size_t ref_ctr_off); + // create a tracepoint fd int add_tracepoint(int fd, int pid, int32_t tracepoint_id); int add_software_perf_event(int cpu, int32_t sample_type, int64_t config); + + // check and attach a perf event to a bpf program int attach_perf_to_bpf(int perf_fd, int bpf_fd); + + // add a attach target to a bpf program without checking the perf event + int add_bpf_prog_attach_target(int perf_fd, int bpf_fd); + + // enable a perf event int perf_event_enable(int fd) const; + + // disable a perf event int perf_event_disable(int fd) const; int add_ringbuf_to_epoll(int ringbuf_fd, int epoll_fd, epoll_data_t extra_data); @@ -104,11 +114,11 @@ class bpftime_shm { // The fake fd should be closed by the caller. void close_fd(int fd); bool is_exist_fake_fd(int fd) const; - + // initialize the shared memory globally - bpftime_shm(); + bpftime_shm(bpftime::shm_open_type type); // initialize the shared memory with a given name - bpftime_shm(const char* shm_name, shm_open_type type); + bpftime_shm(const char *shm_name, shm_open_type type); const handler_manager *get_manager() const; diff --git a/runtime/src/bpftime_shm_json.cpp b/runtime/src/bpftime_shm_json.cpp index a49268a2..fdf9f6d0 100644 --- a/runtime/src/bpftime_shm_json.cpp +++ b/runtime/src/bpftime_shm_json.cpp @@ -50,7 +50,7 @@ static bpf_map_attr json_to_bpf_map_attr(const json &j) } static json -bpf_perf_event_handler_to_json(const bpf_perf_event_handler &handler) +bpf_perf_event_handler_attr_to_json(const bpf_perf_event_handler &handler) { json j; j["type"] = handler.type; @@ -79,15 +79,18 @@ static int import_shm_handler_from_json(bpftime_shm &shm, json value, int fd) int cnt = value["attr"]["cnt"]; std::vector insns; insns.resize(cnt); - int res = hex_string_to_buffer(insns_str, - (unsigned char *)insns.data(), - insns.size() * sizeof(ebpf_inst)); + int res = + hex_string_to_buffer(insns_str, + (unsigned char *)insns.data(), + insns.size() * sizeof(ebpf_inst)); if (res < 0) { spdlog::error("Failed to parse insns in json"); return -1; } - shm.add_bpf_prog(fd, insns.data(), cnt, name.c_str(), type); + for (int perf_fd : value["attr"]["attach_fds"]) { + shm.add_bpf_prog_attach_target(perf_fd, fd); + } } else if (handler_type == "bpf_map_handler") { std::string name = value["name"]; bpf_map_attr attr = json_to_bpf_map_attr(value["attr"]); @@ -133,7 +136,9 @@ int bpftime::bpftime_import_shm_handler_from_json(bpftime_shm &shm, int fd, return import_shm_handler_from_json(shm, fd, j); } -extern "C" int bpftime_import_shm_handler_from_json(int fd, const char *json_string) { +extern "C" int bpftime_import_shm_handler_from_json(int fd, + const char *json_string) +{ return bpftime::bpftime_import_shm_handler_from_json( shm_holder.global_shared_memory, fd, json_string); } @@ -222,8 +227,8 @@ int bpftime::bpftime_export_shm_to_json(const bpftime_shm &shm, std::get(handler); j[std::to_string(i)] = { { "type", "bpf_perf_event_handler" }, - { "attr", - bpf_perf_event_handler_to_json(perf_handler) } + { "attr", bpf_perf_event_handler_attr_to_json( + perf_handler) } }; spdlog::info("bpf_perf_event_handler found at {}", i); } else if (std::holds_alternative(handler)) { diff --git a/runtime/src/handler/handler_manager.cpp b/runtime/src/handler/handler_manager.cpp index 5882ec7d..1c41ca75 100644 --- a/runtime/src/handler/handler_manager.cpp +++ b/runtime/src/handler/handler_manager.cpp @@ -29,13 +29,17 @@ std::size_t handler_manager::size() const return handlers.size(); } -void handler_manager::set_handler(int fd, handler_variant &&handler, +int handler_manager::set_handler(int fd, handler_variant &&handler, managed_shared_memory &memory) { + if (is_allocated(fd)) { + return -ENOENT; + } handlers[fd] = std::move(handler); if (std::holds_alternative(handlers[fd])) { std::get(handlers[fd]).map_init(memory); } + return fd; } bool handler_manager::is_allocated(int fd) const diff --git a/runtime/src/handler/handler_manager.hpp b/runtime/src/handler/handler_manager.hpp index 66e97c50..77741838 100644 --- a/runtime/src/handler/handler_manager.hpp +++ b/runtime/src/handler/handler_manager.hpp @@ -99,7 +99,7 @@ class handler_manager { const handler_variant &operator[](int idx) const; std::size_t size() const; - void set_handler(int fd, handler_variant &&handler, + int set_handler(int fd, handler_variant &&handler, managed_shared_memory &memory); bool is_allocated(int fd) const; diff --git a/runtime/src/handler/map_handler.hpp b/runtime/src/handler/map_handler.hpp index e945ac0b..d08da6ac 100644 --- a/runtime/src/handler/map_handler.hpp +++ b/runtime/src/handler/map_handler.hpp @@ -132,8 +132,48 @@ class bpf_map_handler { // * const void *map_lookup_elem(const void *key, bool from_userspace = false) const; + // * BPF_MAP_UPDATE_ELEM + // * Description + // * Create or update an element (key/value pair) in a specified map. + // * + // * The *flags* argument should be specified as one of the + // * following: + // * + // * **BPF_ANY** + // * Create a new element or update an existing element. + // * **BPF_NOEXIST** + // * Create a new element only if it did not exist. + // * **BPF_EXIST** + // * Update an existing element. + // * **BPF_F_LOCK** + // * Update a spin_lock-ed map element. + // * + // * Return + // * Returns zero on success. On error, -1 is returned and *errno* + // * is set appropriately. + // * + // * May set *errno* to **EINVAL**, **EPERM**, **ENOMEM**, + // * **E2BIG**, **EEXIST**, or **ENOENT**. + // * + // * **E2BIG** + // * The number of elements in the map reached the + // * *max_entries* limit specified at map creation time. + // * **EEXIST** + // * If *flags* specifies **BPF_NOEXIST** and the element + // * with *key* already exists in the map. + // * **ENOENT** + // * If *flags* specifies **BPF_EXIST** and the element with + // * *key* does not exist in the map. + // * long map_update_elem(const void *key, const void *value, uint64_t flags, bool from_userspace = false) const; + // * BPF_MAP_DELETE_ELEM + // * Description + // * Look up and delete an element by key in a specified map. + // * + // * Return + // * Returns zero on success. On error, -1 is returned and *errno* + // * is set appropriately. long map_delete_elem(const void *key, bool from_userspace = false) const; // * BPF_MAP_GET_NEXT_KEY diff --git a/runtime/syscall-server/syscall_server_main.cpp b/runtime/syscall-server/syscall_server_main.cpp index 79dbdcd6..6ca2de95 100644 --- a/runtime/syscall-server/syscall_server_main.cpp +++ b/runtime/syscall-server/syscall_server_main.cpp @@ -11,8 +11,6 @@ #include using namespace bpftime; -const shm_open_type bpftime::global_shm_open_type = shm_open_type::SHM_SERVER; - // global context for bpf syscall server static syscall_context context; diff --git a/runtime/syscall-server/syscall_server_utils.cpp b/runtime/syscall-server/syscall_server_utils.cpp index 422969f5..579f112a 100644 --- a/runtime/syscall-server/syscall_server_utils.cpp +++ b/runtime/syscall-server/syscall_server_utils.cpp @@ -50,7 +50,7 @@ void start_up() 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(); + bpftime_initialize_global_shm(shm_open_type::SHM_REMOVE_AND_CREATE); auto &agent_config = bpftime_get_agent_config(); if (const char *custom_helpers = getenv("BPFTIME_HELPER_GROUPS"); custom_helpers != nullptr) { diff --git a/runtime/test/src/test_attach_replace.cpp b/runtime/test/src/test_attach_replace.cpp index 8adcabbb..83a7660c 100644 --- a/runtime/test/src/test_attach_replace.cpp +++ b/runtime/test/src/test_attach_replace.cpp @@ -12,9 +12,6 @@ using namespace bpftime; -const shm_open_type bpftime::global_shm_open_type = - shm_open_type::SHM_NO_CREATE; - // This is the original function to hook. int my_function(int parm1, const char *str, char c) { diff --git a/runtime/test/src/test_ffi.cpp b/runtime/test/src/test_ffi.cpp index b402b35f..f928b137 100644 --- a/runtime/test/src/test_ffi.cpp +++ b/runtime/test/src/test_ffi.cpp @@ -6,7 +6,6 @@ #include "bpftime_shm.hpp" #include -const shm_open_type bpftime::global_shm_open_type = shm_open_type::SHM_NO_CREATE; const char *offset_record_path = "./test_ffi.off.txt"; const char *btf_path = "./base.btf"; diff --git a/runtime/test/src/test_ffi_register.cpp b/runtime/test/src/test_ffi_register.cpp index 7438e2b6..685b75e5 100644 --- a/runtime/test/src/test_ffi_register.cpp +++ b/runtime/test/src/test_ffi_register.cpp @@ -8,7 +8,6 @@ using namespace bpftime; -const shm_open_type bpftime::global_shm_open_type = shm_open_type::SHM_NO_CREATE; int test_register_ffi() { diff --git a/runtime/test/src/test_helpers.cpp b/runtime/test/src/test_helpers.cpp index dd58200a..5bae0eec 100644 --- a/runtime/test/src/test_helpers.cpp +++ b/runtime/test/src/test_helpers.cpp @@ -9,8 +9,7 @@ using namespace bpftime; -const shm_open_type bpftime::global_shm_open_type = - shm_open_type::SHM_NO_CREATE; + static void dump_type(void *ctx, const char *fmt, va_list args) { diff --git a/runtime/test/src/test_shm_client.cpp b/runtime/test/src/test_shm_client.cpp index 8ad4504c..48ba7a03 100644 --- a/runtime/test/src/test_shm_client.cpp +++ b/runtime/test/src/test_shm_client.cpp @@ -16,14 +16,13 @@ using namespace bpftime; using namespace boost::interprocess; -const shm_open_type bpftime::global_shm_open_type = shm_open_type::SHM_CLIENT; - int main(int argc, const char **argv) { if (argc == 1) { return 0; } int res = 1; + bpftime_initialize_global_shm(shm_open_type::SHM_OPEN_ONLY); bpf_attach_ctx ctx; agent_config config; diff --git a/runtime/test/src/test_shm_hash_maps.cpp b/runtime/test/src/test_shm_hash_maps.cpp index fe3384c4..c4f8494b 100644 --- a/runtime/test/src/test_shm_hash_maps.cpp +++ b/runtime/test/src/test_shm_hash_maps.cpp @@ -14,7 +14,6 @@ const char *HANDLER_NAME = "my_handler"; using namespace boost::interprocess; using namespace bpftime; -const shm_open_type bpftime::global_shm_open_type = shm_open_type::SHM_NO_CREATE; void test_insert_map(int fd, bpftime::handler_manager &manager_ref, managed_shared_memory &segment) diff --git a/runtime/test/src/test_shm_progs_attach.cpp b/runtime/test/src/test_shm_progs_attach.cpp index 4ee70e3e..74122534 100644 --- a/runtime/test/src/test_shm_progs_attach.cpp +++ b/runtime/test/src/test_shm_progs_attach.cpp @@ -17,8 +17,7 @@ using namespace boost::interprocess; using namespace bpftime; -const shm_open_type bpftime::global_shm_open_type = - shm_open_type::SHM_NO_CREATE; + const char *HANDLER_NAME = "my_handler"; const char *SHM_NAME = "my_shm_attach_test"; diff --git a/runtime/test/src/test_shm_server.cpp b/runtime/test/src/test_shm_server.cpp index 5bb31adf..e566dc92 100644 --- a/runtime/test/src/test_shm_server.cpp +++ b/runtime/test/src/test_shm_server.cpp @@ -16,13 +16,12 @@ using namespace bpftime; using namespace boost::interprocess; -const shm_open_type bpftime::global_shm_open_type = shm_open_type::SHM_SERVER; - int main(int argc, const char **argv) { + bpftime_initialize_global_shm(shm_open_type::SHM_REMOVE_AND_CREATE); bpftime::bpf_map_attr attr; attr.type = bpf_map_handler::BPF_MAP_TYPE_HASH; - bpftime_maps_create("test", attr); + bpftime_maps_create(-1, "test", attr); return system( (std::string("./test_shm_client_Tests") + " sub").c_str()); } diff --git a/runtime/unit-test/CMakeLists.txt b/runtime/unit-test/CMakeLists.txt index 6564cfb0..f7640b8c 100644 --- a/runtime/unit-test/CMakeLists.txt +++ b/runtime/unit-test/CMakeLists.txt @@ -13,7 +13,6 @@ set(TEST_SOURCES maps/test_per_cpu_array.cpp maps/test_per_cpu_hash.cpp test_bpftime_shm_json.cpp - test_daemon.cpp attach/test_uprobe_uretprobe.cpp attach/test_function_address_resolve.cpp attach/test_filter_attach.cpp diff --git a/runtime/unit-test/test_bpftime_shm_json.cpp b/runtime/unit-test/test_bpftime_shm_json.cpp index 2b89e570..c9849f60 100644 --- a/runtime/unit-test/test_bpftime_shm_json.cpp +++ b/runtime/unit-test/test_bpftime_shm_json.cpp @@ -57,7 +57,7 @@ const unsigned char bpf_add_mem_64_bit_minimal[] = TEST_CASE("Test bpftime shm json import/export") { - bpftime_shm shm(SHM_NAME, shm_open_type::SHM_SERVER); + bpftime_shm shm(SHM_NAME, shm_open_type::SHM_REMOVE_AND_CREATE); SECTION("Test shm json export") { @@ -79,6 +79,7 @@ TEST_CASE("Test bpftime shm json import/export") .key_size = 4, .value_size = 4, .max_ents = 10 }); + shm.attach_perf_to_bpf(5, 4); int res = bpftime_export_shm_to_json(shm, "/tmp/bpftime_test_shm_json.json"); REQUIRE(res == 0); @@ -86,7 +87,7 @@ TEST_CASE("Test bpftime shm json import/export") SECTION("Test shm json import") { - bpftime_shm shm2(SHM_NAME, shm_open_type::SHM_CLIENT); + bpftime_shm shm2(SHM_NAME, shm_open_type::SHM_OPEN_ONLY); bpftime_import_shm_from_json(shm2, "/tmp/bpftime_test_shm_json.json"); REQUIRE(shm2.is_prog_fd(4)); diff --git a/tools/README.md b/tools/README.md index 140a1693..790d8f0d 100644 --- a/tools/README.md +++ b/tools/README.md @@ -17,5 +17,5 @@ export PATH=$PATH:~/.bpftime ## bpftime tool -Inspect the shared memory status of the target process. +Inspect or operate the shared memory status of the target process. diff --git a/tools/bpftimetool/README.md b/tools/bpftimetool/README.md new file mode 100644 index 00000000..6ac25996 --- /dev/null +++ b/tools/bpftimetool/README.md @@ -0,0 +1,48 @@ +# bpftime tool + +Inspect or operate the shared memory status of the target process. + +You can use bpftime tool to serialize the shared memory status to json, and load and run the json file. + +## Export data in json + +```console +$ ~/.bpftime/bpftimetool export res.json +[2023-10-23 18:45:25.893] [info] Global shm constructed. shm_open_type 1 for bpftime_maps_shm +[2023-10-23 18:45:25.894] [info] bpf_map_handler name=.rodata.str1.1 found at 3 +[2023-10-23 18:45:25.894] [info] find prog fd=4 name=do_uprobe_trace +[2023-10-23 18:45:25.894] [info] bpf_perf_event_handler found at 5 +INFO [93828]: Global shm destructed +``` + +## Import data from json + +```console +SPDLOG_LEVEL=Debug ~/.bpftime/bpftimetool import /home/yunwei/bpftime/tools/bpftimetool/minimal.json +[2023-10-23 19:02:04.955] [info] Global shm constructed. shm_open_type 3 for bpftime_maps_shm +[2023-10-23 19:02:04.955] [info] import handler fd 3 {"attr":{"btf_id":2,"btf_key_type_id":0,"btf_value_type_id":0,"btf_vmlinux_value_type_id":0,"flags":128,"ifindex":0,"key_size":4,"map_extra":0,"map_type":2,"max_entries":1,"value_size":21},"name":".rodata.str1.1","type":"bpf_map_handler"} +[2023-10-23 19:02:04.955] [info] import handler type bpf_map_handler fd 3 +[2023-10-23 19:02:04.956] [info] import handler fd 4 {"attr":{"attach_fds":[5],"cnt":16,"insns":"b701000065642e0a631af8ff0000000018010000756e63200000000063616c6c7b1af0ff0000000018010000746172670000000065745f667b1ae8ff00000000b701000000000000731afcff00000000bfa100000000000007010000e8ffffffb7020000150000008500000006000000b7000000000000009500000000000000","type":0},"name":"do_uprobe_trace","type":"bpf_prog_handler"} +[2023-10-23 19:02:04.956] [info] import handler type bpf_prog_handler fd 4 +[2023-10-23 19:02:04.956] [info] import handler fd 5 {"attr":{"_module_name":"example/minimal/victim","offset":4457,"pid":-1,"ref_ctr_off":0,"tracepoint_id":-1,"type":6},"type":"bpf_perf_event_handler"} +[2023-10-23 19:02:04.956] [info] import handler type bpf_perf_event_handler fd 5 +INFO [99712]: Global shm destructed +``` + +## Run program with bpftime + +```console +$ SPDLOG_LEVEL=Debug bpftime start ./example/minimal/victim +[2023-10-23 19:03:26.105] [info] Entering bpftime agent +[2023-10-23 19:03:26.106] [info] Global shm constructed. shm_open_type 1 for bpftime_maps_shm +[2023-10-23 19:03:26.106] [info] Initializing agent.. +[2023-10-23 19:03:26][info][99986] Executable path: /home/yunwei/bpftime/example/minimal/victim +[2023-10-23 19:03:26][info][99986] Attached 1 uprobe programs to function 5590e1d41169 +[2023-10-23 19:03:26][info][99986] Attach successfully +target_func called. +target_func +target_func called. +target_func +target_func called. +target_func +``` \ No newline at end of file diff --git a/tools/bpftimetool/main.cpp b/tools/bpftimetool/main.cpp index caf30f89..b468ce21 100644 --- a/tools/bpftimetool/main.cpp +++ b/tools/bpftimetool/main.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include using namespace std; using namespace bpftime; @@ -15,42 +17,63 @@ using namespace bpftime; int main(int argc, char *argv[]) { if (argc == 1) { - cerr << "Usage: " << argv[0] << " [load|import|export] ..." - << endl - << "Command-line tool to inspect and manage userspace eBPF objects" << endl; + cerr << "Usage: " << argv[0] << " [load|import|export|remove] ..." + << endl + << "Command-line tool to inspect and manage userspace eBPF objects" + << endl; return 1; } + spdlog::cfg::load_env_levels(); + auto cmd = std::string(argv[1]); if (cmd == "load") { if (argc != 3) { cerr << "Usage: " << argv[0] << " load " - << endl - << "Load a JSON file containing eBPF objects into the global shared memory" << endl; + << endl + << "Load a JSON file containing eBPF objects into the global shared memory" + << endl; return 1; } + bpftime_initialize_global_shm(shm_open_type::SHM_CREATE_OR_OPEN); int fd = atoi(argv[2]); auto json_str = std::string(argv[3]); - return bpftime_import_shm_handler_from_json(fd, json_str.c_str()); + return bpftime_import_shm_handler_from_json(fd, + json_str.c_str()); } else if (cmd == "export") { if (argc != 3) { cerr << "Usage: " << argv[0] << " export " - << endl - << "Export the global shared memory to a JSON file" << endl; + << endl + << "Export the global shared memory to a JSON file" + << endl; return 1; } + bpftime_initialize_global_shm(shm_open_type::SHM_OPEN_ONLY); auto filename = std::string(argv[2]); return bpftime_export_global_shm_to_json(filename.c_str()); } else if (cmd == "import") { if (argc != 3) { cerr << "Usage: " << argv[0] << " import " - << endl - << "Import a JSON file containing eBPF objects into the global shared memory" << endl; + << endl + << "Import a JSON file containing eBPF objects into the global shared memory" + << endl; return 1; } + bpftime_initialize_global_shm(shm_open_type::SHM_CREATE_OR_OPEN); auto filename = std::string(argv[2]); return bpftime_import_global_shm_from_json(filename.c_str()); - } else { + } else if (cmd == "remove") { + if (argc != 2) { + cerr << "Usage: " << argv[0] << " remove" + << endl + << "Remove the global shared memory system wide" + << endl; + return 1; + } + bpftime_remove_global_shm(); + return 0; + } + else { cerr << "Invalid subcommand " << cmd << endl; return 1; } diff --git a/tools/bpftimetool/minimal.json b/tools/bpftimetool/minimal.json new file mode 100644 index 00000000..436c4cea --- /dev/null +++ b/tools/bpftimetool/minimal.json @@ -0,0 +1,42 @@ +{ + "3": { + "attr": { + "btf_id": 2, + "btf_key_type_id": 0, + "btf_value_type_id": 0, + "btf_vmlinux_value_type_id": 0, + "flags": 128, + "ifindex": 0, + "key_size": 4, + "map_extra": 0, + "map_type": 2, + "max_entries": 1, + "value_size": 21 + }, + "name": ".rodata.str1.1", + "type": "bpf_map_handler" + }, + "4": { + "attr": { + "attach_fds": [ + 5 + ], + "cnt": 16, + "insns": "b701000065642e0a631af8ff0000000018010000756e63200000000063616c6c7b1af0ff0000000018010000746172670000000065745f667b1ae8ff00000000b701000000000000731afcff00000000bfa100000000000007010000e8ffffffb7020000150000008500000006000000b7000000000000009500000000000000", + "type": 0 + }, + "name": "do_uprobe_trace", + "type": "bpf_prog_handler" + }, + "5": { + "attr": { + "_module_name": "example/minimal/victim", + "offset": 4457, + "pid": -1, + "ref_ctr_off": 0, + "tracepoint_id": -1, + "type": 6 + }, + "type": "bpf_perf_event_handler" + } +} \ No newline at end of file