diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 90956549..20d367b9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -3,6 +3,9 @@ name: Build and public docker image on: push: branches: "master" + pull_request: + # Why wildcard? Because we want to see test results when examing prs from non-master branches.. + branches: "master" jobs: build-and-push-image: @@ -27,9 +30,13 @@ jobs: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} + - name: build docker + run: | + docker build . - name: Build image and push to GitHub Container Registry uses: docker/build-push-action@v2 + if: github.repository_owner == 'eunomia-bpf' with: # relative path to the place where source code with Dockerfile is located context: ./ diff --git a/.github/workflows/test-runtime.yml b/.github/workflows/test-runtime.yml index f2ed5a18..5e81cddc 100644 --- a/.github/workflows/test-runtime.yml +++ b/.github/workflows/test-runtime.yml @@ -22,6 +22,6 @@ jobs: - name: Build the target run: | CC=gcc-12 CXX=g++-12 make build-unit-test - - name: Run the test + - name: Run the unit test run: | make unit-test diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a20103e..49253fb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,7 @@ set(SPDLOG_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/spdlog/include) # main library add_subdirectory(vm) add_subdirectory(runtime) +add_subdirectory(daemon) # benchmark that requires bpftime libraries add_subdirectory(benchmark) diff --git a/Dockerfile b/Dockerfile index 90941be4..96e07391 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,13 @@ FROM ubuntu:23.04 WORKDIR /bpftime -RUN apt-get update && apt install -y --no-install-recommends \ +RUN apt-get update && apt-get install -y --no-install-recommends \ libelf1 libelf-dev zlib1g-dev make cmake git libboost1.74-all-dev \ binutils-dev libyaml-cpp-dev gcc g++ ca-certificates clang llvm -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +RUN apt-get install -y --no-install-recommends curl && \ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y COPY . . RUN git submodule update --init --recursive ENV CXX=g++ ENV CC=gcc +ENV PATH="${PATH}:/root/.cargo/bin" RUN make release && make install diff --git a/Makefile b/Makefile index a8c95b55..f3066062 100644 --- a/Makefile +++ b/Makefile @@ -1,67 +1,63 @@ -.PHONY: install coverage test docs help build clean build-arm run-arm run-arm64 build-arm64 build-arm32 -.DEFAULT_GOAL := help - -define BROWSER_PYSCRIPT -import os, webbrowser, sys - -try: - from urllib import pathname2url -except: - from urllib.request import pathname2url - -webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) -endef -export BROWSER_PYSCRIPT - -define PRINT_HELP_PYSCRIPT -import re, sys - -for line in sys.stdin: - match = re.match(r'^([a-zA-Z\d_-]+):.*?## (.*)$$', line) - if match: - target, help = match.groups() - print("%-20s %s" % (target, help)) -endef -export PRINT_HELP_PYSCRIPT - -BROWSER := python3 -c "$$BROWSER_PYSCRIPT" -INSTALL_LOCATION := ~/.local - -help: - @python3 -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) - -build-unit-test: - cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=1 -DCMAKE_BUILD_TYPE:STRING=Debug - cmake --build build --config Debug -j --target bpftime_runtime_tests -unit-test: - ./build/runtime/unit-test/bpftime_runtime_tests -test: ## test the package - make -C third_party/libbpf/src - make -C runtime/test/bpf - cp -r runtime/test/bpf/* build/runtime/test/ - cd build/runtime && ctest -VV - -build: ## build the package - cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=1 - cmake --build build --config Debug -j - cd tools/cli-rs && cargo build - -release: ## build the package - cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=0 -DCMAKE_BUILD_TYPE:STRING=Release - cmake --build build --config Release --target install - cd tools/cli-rs && cargo build - -build-vm: ## build only the core library - make -C vm build - -build-llvm: ## build with llvm as jit backend - cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=1 -DBPFTIME_LLVM_JIT=1 - cmake --build build --config Debug - -clean: ## clean the project - rm -rf build - make -C runtime clean - make -C vm clean - -install: release ## Invoke cmake to install.. - cd tools/cli-rs && mkdir -p ~/.bpftime && cp ./target/release/bpftime ~/.bpftime +.PHONY: install coverage test docs help build clean build-arm run-arm run-arm64 build-arm64 build-arm32 +.DEFAULT_GOAL := help + +define BROWSER_PYSCRIPT +import os, webbrowser, sys + +try: + from urllib import pathname2url +except: + from urllib.request import pathname2url + +webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) +endef +export BROWSER_PYSCRIPT + +define PRINT_HELP_PYSCRIPT +import re, sys + +for line in sys.stdin: + match = re.match(r'^([a-zA-Z\d_-]+):.*?## (.*)$$', line) + if match: + target, help = match.groups() + print("%-20s %s" % (target, help)) +endef +export PRINT_HELP_PYSCRIPT + +BROWSER := python3 -c "$$BROWSER_PYSCRIPT" +INSTALL_LOCATION := ~/.local + +help: + @python3 -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) + +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: + ./build/runtime/unit-test/bpftime_runtime_tests + +build: ## build the package + cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=1 + cmake --build build --config Debug + cd tools/cli-rs && cargo build + +release: ## build the package + cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=0 -DCMAKE_BUILD_TYPE:STRING=Release + cmake --build build --config Release --target install + cd tools/cli-rs && cargo build --release + +build-vm: ## build only the core library + make -C vm build + +build-llvm: ## build with llvm as jit backend + cmake -Bbuild -DBPFTIME_ENABLE_UNIT_TESTING=1 -DBPFTIME_LLVM_JIT=1 + cmake --build build --config Debug + +clean: ## clean the project + rm -rf build + make -C runtime clean + make -C vm clean + +install: release ## Invoke cmake to install.. + cd tools/cli-rs && mkdir -p ~/.bpftime && cp ./target/release/bpftime ~/.bpftime diff --git a/benchmark/uprobe/uprobe b/benchmark/uprobe/uprobe deleted file mode 100755 index 7cccbf31..00000000 Binary files a/benchmark/uprobe/uprobe and /dev/null differ diff --git a/benchmark/uretprobe/uretprobe b/benchmark/uretprobe/uretprobe deleted file mode 100755 index 4402af0a..00000000 Binary files a/benchmark/uretprobe/uretprobe and /dev/null differ diff --git a/runtime/bpftime-daemon/.gitignore b/daemon/.gitignore similarity index 100% rename from runtime/bpftime-daemon/.gitignore rename to daemon/.gitignore diff --git a/runtime/bpftime-daemon/CMakeLists.txt b/daemon/CMakeLists.txt similarity index 80% rename from runtime/bpftime-daemon/CMakeLists.txt rename to daemon/CMakeLists.txt index 0bede7cb..a4f461ab 100644 --- a/runtime/bpftime-daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -6,9 +6,9 @@ add_bpf_skel_generating_target(bpftime_daemon_ebpf_skel ${CMAKE_CURRENT_BINARY_D add_dependencies(bpftime_daemon_ebpf_skel bpftime_daemon_ebpf_target) -add_executable(bpftime_daemon bpf-mocker.cpp) -add_dependencies(bpftime_daemon bpftime_daemon_ebpf_skel libbpf) +add_executable(bpftime_daemon main.cpp bpf-mocker.cpp handle_bpf_event.cpp) +add_dependencies(bpftime_daemon bpftime_daemon_ebpf_skel libbpf spdlog::spdlog) 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) +target_link_libraries(bpftime_daemon PRIVATE ${LIBBPF_LIBRARIES} elf z spdlog::spdlog) set_property(TARGET bpftime_daemon PROPERTY CXX_STANDARD 20) diff --git a/runtime/bpftime-daemon/README.md b/daemon/README.md similarity index 100% rename from runtime/bpftime-daemon/README.md rename to daemon/README.md diff --git a/runtime/bpftime-daemon/bpf-mocker-event.h b/daemon/bpf-mocker-event.h similarity index 88% rename from runtime/bpftime-daemon/bpf-mocker-event.h rename to daemon/bpf-mocker-event.h index dfedcca4..1a2d41b8 100644 --- a/runtime/bpftime-daemon/bpf-mocker-event.h +++ b/daemon/bpf-mocker-event.h @@ -12,6 +12,7 @@ enum event_type { SYS_OPEN, + SYS_CLOSE, SYS_BPF, SYS_PERF_EVENT_OPEN, BPF_PROG_LOAD_EVENT, @@ -43,6 +44,10 @@ struct event { struct perf_event_attr attr; int pid; int cpu; + + // uprobe data + uint64_t offset; + char name_or_path[NAME_MAX]; } perf_event_data; struct { @@ -51,6 +56,10 @@ struct event { char prog_name[BPF_OBJ_NAME_LEN]; unsigned int insns[MAX_INSN_SIZE]; } bpf_loaded_prog; + + struct { + int fd; + } close_data; }; }; diff --git a/runtime/bpftime-daemon/bpf-mocker.bpf.c b/daemon/bpf-mocker.bpf.c similarity index 80% rename from runtime/bpftime-daemon/bpf-mocker.bpf.c rename to daemon/bpf-mocker.bpf.c index 54fd7b0c..0c68ae73 100644 --- a/runtime/bpftime-daemon/bpf-mocker.bpf.c +++ b/daemon/bpf-mocker.bpf.c @@ -13,6 +13,7 @@ struct open_args_t { int flags; }; +// track open syscall args struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); @@ -99,7 +100,7 @@ int tracepoint__syscalls__sys_exit_openat(struct trace_event_raw_sys_exit *ctx) struct bpf_args_t { enum bpf_cmd cmd; - void *attr; + union bpf_attr attr; u32 attr_size; }; @@ -208,7 +209,8 @@ int tracepoint__syscalls__sys_enter_bpf(struct trace_event_raw_sys_enter *ctx) /* store arg info for later lookup */ struct bpf_args_t args = {}; args.cmd = (u32)ctx->args[0]; - args.attr = (void *)ctx->args[1]; + bpf_probe_read_user(&args.attr, sizeof(args.attr), + (void *)ctx->args[1]); args.attr_size = (u32)ctx->args[2]; u32 pid = bpf_get_current_pid_tgid() >> 32; @@ -218,6 +220,30 @@ int tracepoint__syscalls__sys_enter_bpf(struct trace_event_raw_sys_enter *ctx) return 0; } +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)) { + return 0; + } + + switch (cmd) { + case BPF_PROG_LOAD: + set_bpf_fd_if_positive(ret); + break; + case BPF_MAP_CREATE: + set_bpf_fd_if_positive(ret); + break; + case BPF_LINK_CREATE: + set_bpf_fd_if_positive(ret); + break; + default: + break; + } + return 0; +} + SEC("tracepoint/syscalls/sys_exit_bpf") int tracepoint__syscalls__sys_exit_bpf(struct trace_event_raw_sys_exit *ctx) { @@ -238,12 +264,16 @@ int tracepoint__syscalls__sys_exit_bpf(struct trace_event_raw_sys_exit *ctx) return 0; } event->type = SYS_BPF; - bpf_probe_read_user_str(&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; + process_bpf_syscall_exit(event->bpf_data.bpf_cmd, &event->bpf_data.attr, + event->bpf_data.attr_size, event->bpf_data.ret, + ctx); + /* emit event */ bpf_ringbuf_submit(event, 0); cleanup: @@ -255,16 +285,25 @@ 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 empty_attr = {}; + struct perf_event_attr new_attr = {}; if (!attr) { return 0; } - // bpf_probe_write_user(attr, &empty_attr, sizeof(empty_attr)); + 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; + // } return 0; } struct perf_event_args_t { - struct perf_event_attr *attr; + struct perf_event_attr attr; int pid; int cpu; }; @@ -288,7 +327,8 @@ int tracepoint__syscalls__sys_enter_perf_event_open( /* store arg info for later lookup */ struct perf_event_args_t args = {}; - args.attr = (void *)ctx->args[0]; + bpf_probe_read_user(&args.attr, sizeof(args.attr), + (void *)ctx->args[0]); args.pid = (int)ctx->args[1]; args.cpu = (int)ctx->args[2]; @@ -314,6 +354,8 @@ int tracepoint__syscalls__sys_exit_perf_event_open( ap = bpf_map_lookup_elem(&perf_event_open_param_start, &pid); if (!ap) return 0; /* missed entry */ + + set_bpf_fd_if_positive(ctx->ret); /* event data */ event = fill_basic_event_info(); @@ -322,8 +364,8 @@ int tracepoint__syscalls__sys_exit_perf_event_open( } event->type = SYS_PERF_EVENT_OPEN; - bpf_probe_read_user_str(&event->perf_event_data.attr, - sizeof(event->perf_event_data.attr), ap->attr); + bpf_probe_read(&event->perf_event_data.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; @@ -335,4 +377,30 @@ int tracepoint__syscalls__sys_exit_perf_event_open( return 0; } +SEC("tracepoint/syscalls/sys_enter_close") +int tracepoint__syscalls__sys_enter_close(struct trace_event_raw_sys_enter *ctx) +{ + struct event *event = NULL; + + if (!filter_target()) { + return 0; + } + int fd = (int)ctx->args[0]; + if (!is_bpf_fd(fd)) { + return 0; + } + /* event data */ + event = fill_basic_event_info(); + if (!event) { + return 0; + } + event->type = SYS_CLOSE; + + event->close_data.fd = fd; + /* emit event */ + bpf_ringbuf_submit(event, 0); + + return 0; +} + char LICENSE[] SEC("license") = "GPL"; diff --git a/daemon/bpf-mocker.cpp b/daemon/bpf-mocker.cpp new file mode 100644 index 00000000..4e2ad50c --- /dev/null +++ b/daemon/bpf-mocker.cpp @@ -0,0 +1,123 @@ +// Description: bpf-mocker daemon +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bpf-mocker-event.h" +#include "bpf-mocker.skel.h" +#include "daemon_config.hpp" +#include "handle_bpf_event.hpp" +#include "daemon.hpp" + +#define NSEC_PER_SEC 1000000000ULL + +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) +{ + if (level == LIBBPF_DEBUG && !verbose) + return 0; + return vfprintf(stderr, format, args); +} + +static void sig_int(int signo) +{ + exiting = 1; +} + + +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); + 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) +{ + LIBBPF_OPTS(bpf_object_open_opts, open_opts); + struct ring_buffer *rb = NULL; + struct bpf_mocker_bpf *obj = NULL; + int err; + + libbpf_set_print(libbpf_print_fn); + + // update handler config + handler = bpf_event_handler(env); + verbose = env.verbose; + + if (signal(SIGINT, sig_int) == SIG_ERR) { + fprintf(stderr, "can't set signal handler: %s\n", + strerror(errno)); + err = 1; + goto cleanup; + } + + obj = bpf_mocker_bpf__open(); + if (!obj) { + 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->uprobe_perf_type = determine_uprobe_perf_type(); + obj->rodata->kprobe_perf_type = determine_kprobe_perf_type(); + + err = bpf_mocker_bpf__load(obj); + if (err) { + fprintf(stderr, "failed to load BPF object: %d\n", err); + goto cleanup; + } + + err = bpf_mocker_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, + NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto cleanup; + } + + /* main: poll */ + while (!exiting) { + err = ring_buffer__poll(rb, 100 /* timeout, ms */); + if (err < 0 && err != -EINTR) { + fprintf(stderr, "error polling perf buffer: %s\n", + strerror(-err)); + goto cleanup; + } + /* reset err to return 0 if exiting */ + err = 0; + } + +cleanup: + ring_buffer__free(rb); + bpf_mocker_bpf__destroy(obj); + + return err != 0; +} diff --git a/runtime/bpftime-daemon/bpf-utils.h b/daemon/bpf-utils.h similarity index 80% rename from runtime/bpftime-daemon/bpf-utils.h rename to daemon/bpf-utils.h index ca1db761..61634606 100644 --- a/runtime/bpftime-daemon/bpf-utils.h +++ b/daemon/bpf-utils.h @@ -14,11 +14,52 @@ 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; diff --git a/daemon/daemon.hpp b/daemon/daemon.hpp new file mode 100644 index 00000000..ac18146b --- /dev/null +++ b/daemon/daemon.hpp @@ -0,0 +1,11 @@ +#ifndef BPFTIME_DAEMON_HPP +#define BPFTIME_DAEMON_HPP + +#include "daemon_config.hpp" + +namespace bpftime +{ +int start_daemon(struct env env); +} // namespace bpftime + +#endif // BPFTIME_DAEMON_HPP \ No newline at end of file diff --git a/daemon/daemon_config.hpp b/daemon/daemon_config.hpp new file mode 100644 index 00000000..bc8caabb --- /dev/null +++ b/daemon/daemon_config.hpp @@ -0,0 +1,14 @@ +#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/handle_bpf_event.cpp b/daemon/handle_bpf_event.cpp new file mode 100644 index 00000000..6568e814 --- /dev/null +++ b/daemon/handle_bpf_event.cpp @@ -0,0 +1,301 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "handle_bpf_event.hpp" +#include "bpf-mocker-event.h" + +using namespace bpftime; + +/* + * this function is expected to parse integer in the range of [0, 2^31-1] from + * given file using scanf format string fmt. If actual parsed value is + * negative, the result might be indistinguishable from error + */ +static int parse_uint_from_file(const char *file, const char *fmt) +{ + int err, ret; + FILE *f; + + f = fopen(file, "re"); + if (!f) { + err = -errno; + fprintf(stderr, "failed to open '%s\n", file); + exit(1); + } + err = fscanf(f, fmt, &ret); + if (err != 1) { + err = err == EOF ? -EIO : -errno; + fprintf(stderr, "failed to parse '%s'\n", file); + fclose(f); + exit(1); + } + fclose(f); + return ret; +} + +int bpftime::determine_kprobe_perf_type(void) +{ + const char *file = "/sys/bus/event_source/devices/kprobe/type"; + + return parse_uint_from_file(file, "%d\n"); +} + +int bpftime::determine_uprobe_perf_type(void) +{ + const char *file = "/sys/bus/event_source/devices/uprobe/type"; + + return parse_uint_from_file(file, "%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; +} + +static const char *bpf_cmd_strings[] = { + "BPF_MAP_CREATE", "BPF_MAP_LOOKUP_ELEM", + "BPF_MAP_UPDATE_ELEM", "BPF_MAP_DELETE_ELEM", + "BPF_MAP_GET_NEXT_KEY", "BPF_PROG_LOAD", + "BPF_OBJ_PIN", "BPF_OBJ_GET", + "BPF_PROG_ATTACH", "BPF_PROG_DETACH", + "BPF_PROG_TEST_RUN", "BPF_PROG_GET_NEXT_ID", + "BPF_MAP_GET_NEXT_ID", "BPF_PROG_GET_FD_BY_ID", + "BPF_MAP_GET_FD_BY_ID", "BPF_OBJ_GET_INFO_BY_FD", + "BPF_PROG_QUERY", "BPF_RAW_TRACEPOINT_OPEN", + "BPF_BTF_LOAD", "BPF_BTF_GET_FD_BY_ID", + "BPF_TASK_FD_QUERY", "BPF_MAP_LOOKUP_AND_DELETE_ELEM", + "BPF_MAP_FREEZE", "BPF_BTF_GET_NEXT_ID", + "BPF_MAP_LOOKUP_BATCH", "BPF_MAP_LOOKUP_AND_DELETE_BATCH", + "BPF_MAP_UPDATE_BATCH", "BPF_MAP_DELETE_BATCH", + "BPF_LINK_CREATE", "BPF_LINK_UPDATE", + "BPF_LINK_GET_FD_BY_ID", "BPF_LINK_GET_NEXT_ID", + "BPF_ENABLE_STATS", "BPF_ITER_CREATE", + "BPF_LINK_DETACH", "BPF_PROG_BIND_MAP", +}; + +static const char *bpf_prog_type_strings[] = { + "BPF_PROG_TYPE_UNSPEC", + "BPF_PROG_TYPE_SOCKET_FILTER", + "BPF_PROG_TYPE_KPROBE", + "BPF_PROG_TYPE_SCHED_CLS", + "BPF_PROG_TYPE_SCHED_ACT", + "BPF_PROG_TYPE_TRACEPOINT", + "BPF_PROG_TYPE_XDP", + "BPF_PROG_TYPE_PERF_EVENT", + "BPF_PROG_TYPE_CGROUP_SKB", + "BPF_PROG_TYPE_CGROUP_SOCK", + "BPF_PROG_TYPE_LWT_IN", + "BPF_PROG_TYPE_LWT_OUT", + "BPF_PROG_TYPE_LWT_XMIT", + "BPF_PROG_TYPE_SOCK_OPS", + "BPF_PROG_TYPE_SK_SKB", + "BPF_PROG_TYPE_CGROUP_DEVICE", + "BPF_PROG_TYPE_SK_MSG", + "BPF_PROG_TYPE_RAW_TRACEPOINT", + "BPF_PROG_TYPE_CGROUP_SOCK_ADDR", + "BPF_PROG_TYPE_LWT_SEG6LOCAL", + "BPF_PROG_TYPE_LIRC_MODE2", + "BPF_PROG_TYPE_SK_REUSEPORT", + "BPF_PROG_TYPE_FLOW_DISSECTOR", + "BPF_PROG_TYPE_CGROUP_SYSCTL", + "BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE", + "BPF_PROG_TYPE_CGROUP_SOCKOPT", + "BPF_PROG_TYPE_TRACING", + "BPF_PROG_TYPE_STRUCT_OPS", + "BPF_PROG_TYPE_EXT", + "BPF_PROG_TYPE_LSM", + "BPF_PROG_TYPE_SK_LOOKUP", + "BPF_PROG_TYPE_SYSCALL", +}; + +static const char *const bpf_map_type_strings[] = { + "BPF_MAP_TYPE_UNSPEC", + "BPF_MAP_TYPE_HASH", + "BPF_MAP_TYPE_ARRAY", + "BPF_MAP_TYPE_PROG_ARRAY", + "BPF_MAP_TYPE_PERF_EVENT_ARRAY", + "BPF_MAP_TYPE_PERCPU_HASH", + "BPF_MAP_TYPE_PERCPU_ARRAY", + "BPF_MAP_TYPE_STACK_TRACE", + "BPF_MAP_TYPE_CGROUP_ARRAY", + "BPF_MAP_TYPE_LRU_HASH", + "BPF_MAP_TYPE_LRU_PERCPU_HASH", + "BPF_MAP_TYPE_LPM_TRIE", + "BPF_MAP_TYPE_ARRAY_OF_MAPS", + "BPF_MAP_TYPE_HASH_OF_MAPS", + "BPF_MAP_TYPE_DEVMAP", + "BPF_MAP_TYPE_SOCKMAP", + "BPF_MAP_TYPE_CPUMAP", + "BPF_MAP_TYPE_XSKMAP", + "BPF_MAP_TYPE_SOCKHASH", + "BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED", + "BPF_MAP_TYPE_CGROUP_STORAGE", + "BPF_MAP_TYPE_REUSEPORT_SOCKARRAY", + "BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE", + "BPF_MAP_TYPE_QUEUE", + "BPF_MAP_TYPE_STACK", + "BPF_MAP_TYPE_SK_STORAGE", + "BPF_MAP_TYPE_DEVMAP_HASH", + "BPF_MAP_TYPE_STRUCT_OPS", + "BPF_MAP_TYPE_RINGBUF", + "BPF_MAP_TYPE_INODE_STORAGE", + "BPF_MAP_TYPE_TASK_STORAGE", + "BPF_MAP_TYPE_BLOOM_FILTER", + "BPF_MAP_TYPE_USER_RINGBUF", + "BPF_MAP_TYPE_CGRP_STORAGE" +}; + +#define BPF_MAP_TYPE_MAX \ + (sizeof(bpf_map_type_strings) / sizeof(bpf_map_type_strings[0])) + +static const char *get_bpf_map_type_string(enum bpf_map_type type) +{ + if (type >= 0 && type < BPF_MAP_TYPE_MAX) { + return bpf_map_type_strings[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); + return 0; +} + +int bpf_event_handler::handle_bpf_event(const struct event *e) +{ + /* prepare fields */ + const char *cmd_str = + e->bpf_data.bpf_cmd >= (sizeof(bpf_cmd_strings) / + sizeof(bpf_cmd_strings[0])) ? + "UNKNOWN COMMAND" : + bpf_cmd_strings[e->bpf_data.bpf_cmd]; + + spdlog::info("BPF {:<6} {:<16} cmd:{:<16} ret:{}", e->pid, e->comm, + cmd_str, e->bpf_data.ret); + + switch (e->bpf_data.bpf_cmd) { + case BPF_MAP_CREATE: + /* code */ + spdlog::info( + " BPF_MAP_CREATE map_type:{:<16} map_name:{:<16}", + get_bpf_map_type_string( + (enum bpf_map_type)e->bpf_data.attr.map_type), + e->bpf_data.attr.map_name); + 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); + break; + default: + break; + } + + return 0; +} + +#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", +}; + +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]; + + /* print output */ + spdlog::info("PERF {:<6} {:<16} type:{:<16} ret:{}\n", e->pid, e->comm, + type_id_str, e->perf_event_data.ret); + return 0; +} + +int bpf_event_handler::handle_load_bpf_prog_event(const struct event *e) +{ + const char *prog_type_str = + e->bpf_loaded_prog.type >= (sizeof(bpf_prog_type_strings) / + sizeof(bpf_prog_type_strings[0])) ? + "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)"; + + /* 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); + return 0; +} + +int bpf_event_handler::handle_event(const struct event *e) +{ + switch (e->type) { + case SYS_OPEN: + return handle_open_events(e); + break; + case SYS_PERF_EVENT_OPEN: + return handle_perf_event(e); + break; + case SYS_BPF: + return handle_bpf_event(e); + break; + case BPF_PROG_LOAD_EVENT: + return handle_load_bpf_prog_event(e); + break; + case SYS_CLOSE: + return handle_close_event(e); + break; + } + return 0; +} + +bpf_event_handler::bpf_event_handler(struct env config) : config(config) +{ + int 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(); + 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/handle_bpf_event.hpp new file mode 100644 index 00000000..a241543d --- /dev/null +++ b/daemon/handle_bpf_event.hpp @@ -0,0 +1,29 @@ +#ifndef BPFTIME_HANDLE_EVENT_HPP +#define BPFTIME_HANDLE_EVENT_HPP + +#include "bpf-mocker-event.h" +#include "daemon_config.hpp" + +namespace bpftime { + +class bpf_event_handler { + struct env config; + + 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); +public: + int handle_event(const struct event *e); + + bpf_event_handler(struct env config); +}; + +int determine_kprobe_perf_type(void); + +int determine_uprobe_perf_type(void); + +} // namespace bpftime + +#endif // BPFTIME_HANDLE_EVENT_HPP diff --git a/daemon/main.cpp b/daemon/main.cpp new file mode 100644 index 00000000..b585bf47 --- /dev/null +++ b/daemon/main.cpp @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +// Copyright (c) 2019 Facebook +// Copyright (c) 2020 Netflix +// +// Based on bpf_mocker(8) from BCC by Brendan Gregg and others. +// 14-Feb-2020 Brendan Gregg Created this. +#include +#include +#include +#include +#include +#include "daemon.hpp" + +using namespace bpftime; + +static struct env 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"; +const char argp_program_doc[] = "Trace and modify bpf syscalls\n"; + +static const struct argp_option opts[] = { + { "pid", 'p', "PID", 0, "Process ID to trace" }, + { "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" }, + {}, +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + static int pos_args; + long int pid, uid; + + switch (key) { + case 'v': + env.verbose = true; + break; + case 'x': + env.failed = true; + break; + case 'o': + env.show_open = true; + 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 'u': + errno = 0; + uid = strtol(arg, NULL, 10); + if (errno || uid < 0 || uid >= -1) { + fprintf(stderr, "Invalid UID %s\n", arg); + argp_usage(state); + } + env.uid = uid; + break; + 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; +} + +int main(int argc, char **argv) +{ + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + int err; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + return start_daemon(env); +} diff --git a/documents/build-and-test.md b/documents/build-and-test.md index b7d677af..0852bfa7 100644 --- a/documents/build-and-test.md +++ b/documents/build-and-test.md @@ -59,5 +59,5 @@ make build-llvm # build the vm with llvm jit Run the test suite for runtime to validate the implementation: ```bash -make test +make unit-test ``` diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 45eb5e44..4f539f3a 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -170,7 +170,6 @@ add_subdirectory(object) add_subdirectory(agent) add_subdirectory(syscall-server) add_subdirectory(agent-transformer) -add_subdirectory(bpftime-daemon) # # Unit testing setup diff --git a/runtime/bpftime-daemon/bpf-mocker.cpp b/runtime/bpftime-daemon/bpf-mocker.cpp deleted file mode 100644 index e2f5f1df..00000000 --- a/runtime/bpftime-daemon/bpf-mocker.cpp +++ /dev/null @@ -1,421 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -// Copyright (c) 2019 Facebook -// Copyright (c) 2020 Netflix -// -// Based on bpf_mocker(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 -#include "bpf-mocker-event.h" -#include "bpf-mocker.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; - -static struct env { - pid_t pid; - uid_t uid; - bool verbose; - bool failed; - bool show_open; -} env = { .uid = INVALID_UID }; - -const char *argp_program_version = "bpftime-daemon 0.1"; -const char *argp_program_bug_address = "https://github.com/eunomia-bpf/bpftime"; -const char argp_program_doc[] = "Trace and modify bpf syscalls\n"; - -static const struct argp_option opts[] = { - { "pid", 'p', "PID", 0, "Process ID to trace" }, - { "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" }, - {}, -}; - -static error_t parse_arg(int key, char *arg, struct argp_state *state) -{ - static int pos_args; - long int pid, uid; - - switch (key) { - case 'v': - env.verbose = true; - break; - case 'x': - env.failed = true; - break; - case 'o': - env.show_open = true; - 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 '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; - 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; -} - -static int handle_open_events(const struct event *e) -{ - struct tm *tm; - char ts[32]; - time_t t; - int fd, err; - if (!env.show_open) { - return 0; - } - - /* prepare fields */ - time(&t); - tm = localtime(&t); - strftime(ts, sizeof(ts), "%H:%M:%S", tm); - if (e->open_data.ret >= 0) { - fd = e->open_data.ret; - err = 0; - } else { - fd = -1; - err = -e->open_data.ret; - } - - /* print output */ - printf("OPEN %-6d %-16s %3d %3d ", e->pid, e->comm, fd, err); - printf("%s\n", e->open_data.fname); - return 0; -} - -const char *bpf_cmd_strings[] = { - "BPF_MAP_CREATE", "BPF_MAP_LOOKUP_ELEM", - "BPF_MAP_UPDATE_ELEM", "BPF_MAP_DELETE_ELEM", - "BPF_MAP_GET_NEXT_KEY", "BPF_PROG_LOAD", - "BPF_OBJ_PIN", "BPF_OBJ_GET", - "BPF_PROG_ATTACH", "BPF_PROG_DETACH", - "BPF_PROG_TEST_RUN", "BPF_PROG_GET_NEXT_ID", - "BPF_MAP_GET_NEXT_ID", "BPF_PROG_GET_FD_BY_ID", - "BPF_MAP_GET_FD_BY_ID", "BPF_OBJ_GET_INFO_BY_FD", - "BPF_PROG_QUERY", "BPF_RAW_TRACEPOINT_OPEN", - "BPF_BTF_LOAD", "BPF_BTF_GET_FD_BY_ID", - "BPF_TASK_FD_QUERY", "BPF_MAP_LOOKUP_AND_DELETE_ELEM", - "BPF_MAP_FREEZE", "BPF_BTF_GET_NEXT_ID", - "BPF_MAP_LOOKUP_BATCH", "BPF_MAP_LOOKUP_AND_DELETE_BATCH", - "BPF_MAP_UPDATE_BATCH", "BPF_MAP_DELETE_BATCH", - "BPF_LINK_CREATE", "BPF_LINK_UPDATE", - "BPF_LINK_GET_FD_BY_ID", "BPF_LINK_GET_NEXT_ID", - "BPF_ENABLE_STATS", "BPF_ITER_CREATE", - "BPF_LINK_DETACH", "BPF_PROG_BIND_MAP", -}; - -const char* bpf_prog_type_strings[] = { - "BPF_PROG_TYPE_UNSPEC", - "BPF_PROG_TYPE_SOCKET_FILTER", - "BPF_PROG_TYPE_KPROBE", - "BPF_PROG_TYPE_SCHED_CLS", - "BPF_PROG_TYPE_SCHED_ACT", - "BPF_PROG_TYPE_TRACEPOINT", - "BPF_PROG_TYPE_XDP", - "BPF_PROG_TYPE_PERF_EVENT", - "BPF_PROG_TYPE_CGROUP_SKB", - "BPF_PROG_TYPE_CGROUP_SOCK", - "BPF_PROG_TYPE_LWT_IN", - "BPF_PROG_TYPE_LWT_OUT", - "BPF_PROG_TYPE_LWT_XMIT", - "BPF_PROG_TYPE_SOCK_OPS", - "BPF_PROG_TYPE_SK_SKB", - "BPF_PROG_TYPE_CGROUP_DEVICE", - "BPF_PROG_TYPE_SK_MSG", - "BPF_PROG_TYPE_RAW_TRACEPOINT", - "BPF_PROG_TYPE_CGROUP_SOCK_ADDR", - "BPF_PROG_TYPE_LWT_SEG6LOCAL", - "BPF_PROG_TYPE_LIRC_MODE2", - "BPF_PROG_TYPE_SK_REUSEPORT", - "BPF_PROG_TYPE_FLOW_DISSECTOR", - "BPF_PROG_TYPE_CGROUP_SYSCTL", - "BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE", - "BPF_PROG_TYPE_CGROUP_SOCKOPT", - "BPF_PROG_TYPE_TRACING", - "BPF_PROG_TYPE_STRUCT_OPS", - "BPF_PROG_TYPE_EXT", - "BPF_PROG_TYPE_LSM", - "BPF_PROG_TYPE_SK_LOOKUP", - "BPF_PROG_TYPE_SYSCALL", -}; - -static int handle_bpf_event(const struct event *e) -{ - struct tm *tm; - char ts[32]; - time_t t; - - /* prepare fields */ - time(&t); - tm = localtime(&t); - strftime(ts, sizeof(ts), "%H:%M:%S", tm); - - const char *cmd_str = e->bpf_data.bpf_cmd >= (sizeof(bpf_cmd_strings) / - sizeof(bpf_cmd_strings[0])) ? - "UNKNOWN COMMAND" : - bpf_cmd_strings[e->bpf_data.bpf_cmd]; - - /* print output */ - printf("BPF %-6d %-16s %-16s %d\n", e->pid, e->comm, cmd_str, e->bpf_data.ret); - return 0; -} - -#define PERF_TYPE_MAX_ID 16 -const char* perf_type_id_strings[PERF_TYPE_MAX_ID] = { - [PERF_TYPE_HARDWARE] = "PERF_TYPE_HARDWARE", - [PERF_TYPE_SOFTWARE] = "PERF_TYPE_SOFTWARE", - [PERF_TYPE_TRACEPOINT] = "PERF_TYPE_TRACEPOINT", - [PERF_TYPE_HW_CACHE] = "PERF_TYPE_HW_CACHE", - [PERF_TYPE_RAW] = "PERF_TYPE_RAW", - [PERF_TYPE_BREAKPOINT] = "PERF_TYPE_BREAKPOINT", -}; - -static int handle_perf_event(const struct event *e) -{ - struct tm *tm; - char ts[32]; - time_t t; - - /* prepare fields */ - time(&t); - tm = localtime(&t); - strftime(ts, sizeof(ts), "%H:%M:%S", tm); - - 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]; - - /* print output */ - printf("PERF %-6d %-16s %-8s %d\n", e->pid, e->comm, type_id_str, e->perf_event_data.pid); - return 0; -} - - -/* - * this function is expected to parse integer in the range of [0, 2^31-1] from - * given file using scanf format string fmt. If actual parsed value is - * negative, the result might be indistinguishable from error - */ -static int parse_uint_from_file(const char *file, const char *fmt) -{ - int err, ret; - FILE *f; - - f = fopen(file, "re"); - if (!f) { - err = -errno; - fprintf(stderr, "failed to open '%s\n", file); - return err; - } - err = fscanf(f, fmt, &ret); - if (err != 1) { - err = err == EOF ? -EIO : -errno; - fprintf(stderr,"failed to parse '%s'\n", file); - fclose(f); - return err; - } - fclose(f); - return ret; -} - -static int determine_kprobe_perf_type(void) -{ - const char *file = "/sys/bus/event_source/devices/kprobe/type"; - - return parse_uint_from_file(file, "%d\n"); -} - -static int determine_uprobe_perf_type(void) -{ - const char *file = "/sys/bus/event_source/devices/uprobe/type"; - - return parse_uint_from_file(file, "%d\n"); -} - - -static int handle_load_bpf_prog_event(const struct event *e) -{ - struct tm *tm; - char ts[32]; - time_t t; - - /* prepare fields */ - time(&t); - tm = localtime(&t); - strftime(ts, sizeof(ts), "%H:%M:%S", tm); - - - const char *prog_type_str = e->bpf_loaded_prog.type >= (sizeof(bpf_prog_type_strings) / - sizeof(bpf_prog_type_strings[0])) ? - "UNKNOWN PROG TYPE" : - bpf_prog_type_strings[e->bpf_loaded_prog.type]; - - /* print output */ - printf("LOAD %-6d %-16s %-16s %-16s %d\n", e->pid, e->comm, prog_type_str, e->bpf_loaded_prog.prog_name, e->bpf_loaded_prog.insn_cnt); - return 0; -} - -static int handle_event_rb(void *ctx, void *data, size_t data_sz) -{ - const struct event *e = (const struct event *)data; - switch (e->type) { - case SYS_OPEN: - return handle_open_events(e); - break; - case SYS_PERF_EVENT_OPEN: - return handle_perf_event(e); - break; - case SYS_BPF: - return handle_bpf_event(e); - break; - case BPF_PROG_LOAD_EVENT: - return handle_load_bpf_prog_event(e); - break; - } - 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 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 perf_buffer *pb = NULL; - struct ring_buffer *rb = NULL; - struct bpf_mocker_bpf *obj = NULL; - int err; - - libbpf_set_print(libbpf_print_fn); - err = argp_parse(&argp, argc, argv, 0, NULL, NULL); - if (err) - return err; - - obj = bpf_mocker_bpf__open(); - if (!obj) { - 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; - - perf_type_id_strings[determine_uprobe_perf_type()] = "PERF_TYPE_UPROBE"; - perf_type_id_strings[determine_kprobe_perf_type()] = "PERF_TYPE_KPROBE"; - - err = bpf_mocker_bpf__load(obj); - if (err) { - fprintf(stderr, "failed to load BPF object: %d\n", err); - goto cleanup; - } - - err = bpf_mocker_bpf__attach(obj); - if (err) { - fprintf(stderr, "failed to attach BPF programs\n"); - goto cleanup; - } - /* print headers */ - printf("%-6s %-16s %3s %3s ", "PID", "COMM", "FD", "ERR"); - printf("%s", "PATH"); - printf("\n"); - - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(obj->maps.rb), handle_event_rb, NULL, - NULL); - if (!rb) { - err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); - goto cleanup; - } - - 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) { - err = ring_buffer__poll(rb, 100 /* timeout, ms */); - if (err < 0 && err != -EINTR) { - fprintf(stderr, "error polling perf buffer: %s\n", - strerror(-err)); - goto cleanup; - } - /* reset err to return 0 if exiting */ - err = 0; - } - -cleanup: - ring_buffer__free(rb); - bpf_mocker_bpf__destroy(obj); - - return err != 0; -} diff --git a/runtime/bpftime-daemon/handle_bpf_event.cpp b/runtime/bpftime-daemon/handle_bpf_event.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/runtime/bpftime-daemon/handle_event.hpp b/runtime/bpftime-daemon/handle_event.hpp deleted file mode 100644 index 0818a691..00000000 --- a/runtime/bpftime-daemon/handle_event.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef BPFTIME_HANDLE_EVENT_HPP -#define BPFTIME_HANDLE_EVENT_HPP - - -#endif // BPFTIME_HANDLE_EVENT_HPP diff --git a/runtime/include/bpftime_shm.hpp b/runtime/include/bpftime_shm.hpp index bf7618c6..5528948b 100644 --- a/runtime/include/bpftime_shm.hpp +++ b/runtime/include/bpftime_shm.hpp @@ -41,41 +41,66 @@ bpftime::agent_config &bpftime_get_agent_config(); } // namespace bpftime 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_destroy_global_shm(); + +// create a bpf link in the global shared memory int bpftime_link_create(int prog_fd, int target_fd); + +// create a bpf prog in the global shared memory int bpftime_progs_create(const ebpf_inst *insn, size_t insn_cnt, const char *prog_name, int prog_type); +// create a bpf map in the global shared memory int bpftime_maps_create(const char *name, bpftime::bpf_map_attr attr); +// get the bpf map info from the global shared memory int bpftime_map_get_info(int fd, bpftime::bpf_map_attr *out_attr, const char **out_name, int *type); +// get the map value size from the global shared memory by fd uint32_t bpftime_map_value_size_from_syscall(int fd); -int bpftime_map_get_next_key_from_helper(int fd, const void *key, - void *next_key); -const void *bpftime_map_lookup_elem_from_helper(int fd, const void *key); -long bpftime_map_update_elem_from_helper(int fd, const void *key, - const void *value, uint64_t flags); -long bpftime_map_delete_elem_from_helper(int fd, const void *key); -int bpftime_map_get_next_key_from_syscall(int fd, const void *key, +// used by bpf_helper to get the next key +int bpftime_helper_map_get_next_key(int fd, const void *key, void *next_key); -const void *bpftime_map_lookup_elem_from_syscall(int fd, const void *key); -long bpftime_map_update_elem_from_syscall(int fd, const void *key, +// used by bpf_helper to lookup the elem +const void *bpftime_helper_map_lookup_elem(int fd, const void *key); +// used by bpf_helper to update the elem +long bpftime_helper_map_update_elem(int fd, const void *key, const void *value, uint64_t flags); -long bpftime_map_delete_elem_from_syscall(int fd, const void *key); +// used by bpf_helper to delete the elem +long bpftime_helper_map_delete_elem(int fd, const void *key); +// use from bpf syscall to get the next key +int bpftime_map_get_next_key(int fd, const void *key, + void *next_key); +// use from bpf syscall to lookup the elem +const void *bpftime_map_lookup_elem(int fd, const void *key); +// use from bpf syscall to update the elem +long bpftime_map_update_elem(int fd, const void *key, + const void *value, uint64_t flags); +// use from bpf syscall to delete the elem +long bpftime_map_delete_elem(int fd, const void *key); +// create uprobe in the global shared memory int bpftime_uprobe_create(int pid, const char *name, uint64_t offset, bool retprobe, size_t ref_ctr_off); +// create tracepoint in the global shared memory int bpftime_tracepoint_create(int pid, int32_t tp_id); + int bpftime_perf_event_enable(int fd); int bpftime_perf_event_disable(int fd); + int bpftime_attach_perf_to_bpf(int perf_fd, int bpf_fd); int bpftime_add_ringbuf_fd_to_epoll(int ringbuf_fd, int epoll_fd, epoll_data_t extra_data); int bpftime_add_software_perf_event_fd_to_epoll(int swpe_fd, int epoll_fd, epoll_data_t extra_data); + int bpftime_epoll_create(); int bpftime_is_ringbuf_map(int fd); void *bpftime_get_ringbuf_consumer_page(int ringbuf_fd); @@ -83,16 +108,20 @@ void *bpftime_get_ringbuf_producer_page(int ringbuf_fd); int bpftime_is_array_map(int fd); int bpftime_is_epoll_handler(int fd); void *bpftime_get_array_map_raw_data(int fd); + void bpftime_close(int fd); + void *bpftime_ringbuf_reserve(int fd, uint64_t size); void bpftime_ringbuf_submit(int fd, void *data, int discard); int bpftime_epoll_wait(int fd, struct epoll_event *out_evts, int max_evt, int timeout); + int bpftime_add_software_perf_event(int cpu, int32_t sample_type, int64_t config); int bpftime_is_software_perf_event(int fd); void *bpftime_get_software_perf_event_raw_buffer(int fd, size_t expected_size); int bpftime_perf_event_output(int fd, const void *buf, size_t sz); + } #endif // BPFTIME_SHM_CPP_H diff --git a/runtime/src/attach/bpf_attach_ctx.cpp b/runtime/src/attach/bpf_attach_ctx.cpp index 47d4f31c..9965e630 100644 --- a/runtime/src/attach/bpf_attach_ctx.cpp +++ b/runtime/src/attach/bpf_attach_ctx.cpp @@ -168,6 +168,7 @@ int bpf_attach_ctx::init_attach_ctx_from_handlers( handler_prog_fds; // First, we create programs for (std::size_t i = 0; i < manager->size(); i++) { + // skip uninitialized handlers if (!manager->is_allocated(i)) { continue; } diff --git a/runtime/src/bpf_helper.cpp b/runtime/src/bpf_helper.cpp index 984257ad..06967ef9 100644 --- a/runtime/src/bpf_helper.cpp +++ b/runtime/src/bpf_helper.cpp @@ -115,7 +115,7 @@ uint64_t bpftime_get_current_comm(uint64_t buf, uint64_t size, uint64_t, uint64_t bpftime_map_lookup_elem_helper(uint64_t map, uint64_t key, uint64_t, uint64_t, uint64_t) { - return (uint64_t)bpftime_map_lookup_elem_from_helper(map >> 32, + return (uint64_t)bpftime_helper_map_lookup_elem(map >> 32, (void *)key); } @@ -123,14 +123,14 @@ uint64_t bpftime_map_update_elem_helper(uint64_t map, uint64_t key, uint64_t value, uint64_t flags, uint64_t) { - return (uint64_t)bpftime_map_update_elem_from_helper( + return (uint64_t)bpftime_helper_map_update_elem( map >> 32, (void *)key, (void *)value, flags); } uint64_t bpftime_map_delete_elem_helper(uint64_t map, uint64_t key, uint64_t, uint64_t, uint64_t) { - return (uint64_t)bpftime_map_delete_elem_from_helper(map >> 32, + return (uint64_t)bpftime_helper_map_delete_elem(map >> 32, (void *)key); } @@ -225,7 +225,7 @@ uint64_t bpf_perf_event_output(uint64_t ctx, uint64_t map, uint64_t flags, } int fd = map >> 32; const int32_t *val_ptr = - (int32_t *)(uintptr_t)bpftime_map_lookup_elem_from_helper( + (int32_t *)(uintptr_t)bpftime_helper_map_lookup_elem( fd, ¤t_cpu); if (val_ptr == nullptr) { spdlog::error("Invalid map fd for perf event output: {}", fd); diff --git a/runtime/src/bpf_map/map_common_def.hpp b/runtime/src/bpf_map/map_common_def.hpp index 45449334..bae31b2f 100644 --- a/runtime/src/bpf_map/map_common_def.hpp +++ b/runtime/src/bpf_map/map_common_def.hpp @@ -55,6 +55,7 @@ inline void ensure_on_certain_cpu(int cpu, std::function func) func(); sched_setaffinity(0, sizeof(orig), &orig); } + struct bytes_vec_hasher { size_t operator()(bytes_vec const &vec) const { diff --git a/runtime/src/bpf_map/per_cpu_array_map.cpp b/runtime/src/bpf_map/per_cpu_array_map.cpp index 457f3d4e..ed47e4e7 100644 --- a/runtime/src/bpf_map/per_cpu_array_map.cpp +++ b/runtime/src/bpf_map/per_cpu_array_map.cpp @@ -6,6 +6,7 @@ #include namespace bpftime { + per_cpu_array_map_impl::per_cpu_array_map_impl( boost::interprocess::managed_shared_memory &memory, uint32_t value_size, uint32_t max_entries, uint32_t ncpu) diff --git a/runtime/src/bpf_map/per_cpu_hash_map.hpp b/runtime/src/bpf_map/per_cpu_hash_map.hpp index 8274bb4f..4bb71689 100644 --- a/runtime/src/bpf_map/per_cpu_hash_map.hpp +++ b/runtime/src/bpf_map/per_cpu_hash_map.hpp @@ -8,6 +8,7 @@ #include #include #include + namespace bpftime { diff --git a/runtime/src/bpftime_shm.cpp b/runtime/src/bpftime_shm.cpp index 5d7c77be..82e9eb66 100644 --- a/runtime/src/bpftime_shm.cpp +++ b/runtime/src/bpftime_shm.cpp @@ -33,48 +33,48 @@ uint32_t bpftime_map_value_size_from_syscall(int fd) return shm_holder.global_shared_memory.bpf_map_value_size(fd); } -const void *bpftime_map_lookup_elem_from_helper(int fd, const void *key) +const void *bpftime_helper_map_lookup_elem(int fd, const void *key) { return shm_holder.global_shared_memory.bpf_map_lookup_elem(fd, key, false); } -long bpftime_map_update_elem_from_helper(int fd, const void *key, +long bpftime_helper_map_update_elem(int fd, const void *key, const void *value, uint64_t flags) { return shm_holder.global_shared_memory.bpf_map_update_elem( fd, key, value, flags, false); } -long bpftime_map_delete_elem_from_helper(int fd, const void *key) +long bpftime_helper_map_delete_elem(int fd, const void *key) { return shm_holder.global_shared_memory.bpf_delete_elem(fd, key, false); } -int bpftime_map_get_next_key_from_helper(int fd, const void *key, +int bpftime_helper_map_get_next_key(int fd, const void *key, void *next_key) { return shm_holder.global_shared_memory.bpf_map_get_next_key( fd, key, next_key, false); } -const void *bpftime_map_lookup_elem_from_syscall(int fd, const void *key) +const void *bpftime_map_lookup_elem(int fd, const void *key) { return shm_holder.global_shared_memory.bpf_map_lookup_elem(fd, key, true); } -long bpftime_map_update_elem_from_syscall(int fd, const void *key, +long bpftime_map_update_elem(int fd, const void *key, const void *value, uint64_t flags) { return shm_holder.global_shared_memory.bpf_map_update_elem( fd, key, value, flags, true); } -long bpftime_map_delete_elem_from_syscall(int fd, const void *key) +long bpftime_map_delete_elem(int fd, const void *key) { return shm_holder.global_shared_memory.bpf_delete_elem(fd, key, true); } -int bpftime_map_get_next_key_from_syscall(int fd, const void *key, +int bpftime_map_get_next_key(int fd, const void *key, void *next_key) { return shm_holder.global_shared_memory.bpf_map_get_next_key( diff --git a/runtime/src/bpftime_shm_internal.cpp b/runtime/src/bpftime_shm_internal.cpp index 804c8e19..251588a5 100644 --- a/runtime/src/bpftime_shm_internal.cpp +++ b/runtime/src/bpftime_shm_internal.cpp @@ -18,6 +18,7 @@ void bpftime_initialize_global_shm() new (&shm_holder.global_shared_memory) bpftime_shm; global_shm_initialized = true; } + void bpftime_destroy_global_shm() { using namespace bpftime; @@ -124,6 +125,7 @@ int bpftime_shm::add_uprobe(int pid, const char *name, uint64_t offset, segment); return fd; } + int bpftime_shm::add_tracepoint(int pid, int32_t tracepoint_id) { int fd = open_fake_fd(); @@ -133,6 +135,7 @@ int bpftime_shm::add_tracepoint(int pid, int32_t tracepoint_id) segment); return fd; } + int bpftime_shm::add_software_perf_event(int cpu, int32_t sample_type, int64_t config) { diff --git a/runtime/src/bpftime_shm_internal.hpp b/runtime/src/bpftime_shm_internal.hpp index 8a6bb321..4efb9100 100644 --- a/runtime/src/bpftime_shm_internal.hpp +++ b/runtime/src/bpftime_shm_internal.hpp @@ -123,6 +123,5 @@ union bpftime_shm_holder { extern bpftime_shm_holder shm_holder; } // namespace bpftime -extern "C" void bpftime_initialize_global_shm(); -extern "C" void bpftime_destroy_global_shm(); + #endif diff --git a/runtime/syscall-server/syscall_context.cpp b/runtime/syscall-server/syscall_context.cpp index f8d83a27..9d1a85c7 100644 --- a/runtime/syscall-server/syscall_context.cpp +++ b/runtime/syscall-server/syscall_context.cpp @@ -66,7 +66,7 @@ long syscall_context::handle_sysbpf(int cmd, union bpf_attr *attr, size_t size) // we should write the bytes of the matched value to the pointer // that user gave us. So here needs a memcpy to achive such // thing. - auto value_ptr = bpftime_map_lookup_elem_from_syscall( + auto value_ptr = bpftime_map_lookup_elem( attr->map_fd, (const void *)(uintptr_t)attr->key); if (value_ptr == nullptr) { errno = ENOENT; @@ -78,19 +78,19 @@ long syscall_context::handle_sysbpf(int cmd, union bpf_attr *attr, size_t size) } case BPF_MAP_UPDATE_ELEM: { spdlog::debug("Updating map"); - return bpftime_map_update_elem_from_syscall( + return bpftime_map_update_elem( attr->map_fd, (const void *)(uintptr_t)attr->key, (const void *)(uintptr_t)attr->value, (uint64_t)attr->flags); } case BPF_MAP_DELETE_ELEM: { spdlog::debug("Deleting map"); - return bpftime_map_delete_elem_from_syscall( + return bpftime_map_delete_elem( attr->map_fd, (const void *)(uintptr_t)attr->key); } case BPF_MAP_GET_NEXT_KEY: { spdlog::debug("Getting next key"); - return (long)(uintptr_t)bpftime_map_get_next_key_from_syscall( + return (long)(uintptr_t)bpftime_map_get_next_key( attr->map_fd, (const void *)(uintptr_t)attr->key, (void *)(uintptr_t)attr->next_key); } diff --git a/runtime/unit-test/CMakeLists.txt b/runtime/unit-test/CMakeLists.txt index 2292df9f..d88da453 100644 --- a/runtime/unit-test/CMakeLists.txt +++ b/runtime/unit-test/CMakeLists.txt @@ -32,7 +32,6 @@ add_test(NAME bpftime_runtime_tests COMMAND bpftime_runtime_tests) # These are necessary ebpf program required by the test set(used_ebpf_programs uprobe - # replace # Not used now filter) diff --git a/runtime/unit-test/assets/uprobe.bpf.c b/runtime/unit-test/assets/uprobe.bpf.c index 130bc45c..96f50aad 100644 --- a/runtime/unit-test/assets/uprobe.bpf.c +++ b/runtime/unit-test/assets/uprobe.bpf.c @@ -1,7 +1,7 @@ #define BPF_NO_GLOBAL_DATA #include "vmlinux.h" -#include "bpf/bpf_tracing.h" -#include "bpf/bpf_helpers.h" +#include +#include SEC("uprobe") int BPF_UPROBE(my_function_uprobe, int parm1, char* str, char c) { diff --git a/runtime/unit-test/test_daemon.cpp b/runtime/unit-test/test_daemon.cpp new file mode 100644 index 00000000..5315d9e0 --- /dev/null +++ b/runtime/unit-test/test_daemon.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +#if !defined(__x86_64__) && defined(_M_X64) +#error Only supports x86_64 +#endif +using namespace bpftime; + + +TEST_CASE("Test attaching filter programs and revert") +{ + +} diff --git a/tools/README.md b/tools/README.md index 41861080..140a1693 100644 --- a/tools/README.md +++ b/tools/README.md @@ -2,9 +2,9 @@ Additional tools for bpftime to improve the user experience. -## cli-rs +## bpftime cli -Use for inject dynamic lib of runtime into target process, and a wrapper and LD_PRELOAD for `bpftime` libraries. +Use for inject dynamic lib of runtime into target process, a wrapper and `LD_PRELOAD` for `bpftime` libraries. This is used as a high-level interface for `bpftime`. ## Install the cli tools an libraries @@ -14,3 +14,8 @@ cd tools/cli-rs && cargo build --release mkdir ~/.bpftime && cp ./target/release/bpftime ~/.bpftime export PATH=$PATH:~/.bpftime ``` + +## bpftime tool + +Inspect the shared memory status of the target process. + diff --git a/tools/bpftimetool/CMakeLists.txt b/tools/bpftimetool/CMakeLists.txt new file mode 100644 index 00000000..ab68c7cd --- /dev/null +++ b/tools/bpftimetool/CMakeLists.txt @@ -0,0 +1,29 @@ +add_executable(bpftimetool + main.cpp +) + +find_package(Boost REQUIRED COMPONENTS program_options) + +add_dependencies(bpftime FridaCore bpftime-object) +target_include_directories(bpftime PUBLIC + + set(LIBBPF_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/../../runtime/object/libbpf) + ${FRIDA_CORE_INSTALL_DIR} + ../../runtime/object + ../../third_party/ + ../../runtime/include + ${LIBBPF_INCLUDE_DIRS}/uapi + ${LIBBPF_INCLUDE_DIRS} +) + +target_link_libraries(bpftime + ${FRIDA_CORE_INSTALL_DIR}/libfrida-core.a + -lpthread + -lm + -ldl + runtime + bpftime-object + Boost::program_options +) + +install(TARGETS bpftime CONFIGURATIONS Release Debug DESTINATION ~/.bpftime) diff --git a/tools/bpftimetool/main.cpp b/tools/bpftimetool/main.cpp new file mode 100644 index 00000000..89c565eb --- /dev/null +++ b/tools/bpftimetool/main.cpp @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern "C" { +#include +#include +#include +} + +const char *DEFAULT_INSTALLATION_LOCATION = "~/.bpftime"; + +using namespace std; + +const std::string version = "1.0.0"; +const bpftime::shm_open_type bpftime::global_shm_open_type = + shm_open_type::SHM_NO_CREATE; + +struct cli_config { + bool dry_run = false; + bool benchmark = false; + bool kernel_uprobe = false; + int pid = 0; + std::string input_file; + int target_pid; + std::string agent_dynlib_path; + std::string bpf_object_path; + bool inject_client = false; + bool inject_server = false; +}; + +using libbpf_object_ptr = + std::unique_ptr; + +// open, load and attach the +static libbpf_object_ptr open_and_attach_libbpf_object(const char *filename) +{ + struct bpf_object *obj; + struct bpf_program *prog; + // struct bpf_link *link; + int err; + + obj = bpf_object__open(filename); + if (libbpf_get_error(obj)) { + fprintf(stderr, "Error opening BPF object file: %s\n", + filename); + return { nullptr, bpf_object__close }; + } + libbpf_object_ptr obj_ptr(obj, bpf_object__close); + + err = bpf_object__load(obj); + if (err) { + fprintf(stderr, "Error loading BPF object file: %s\n", + filename); + return { nullptr, bpf_object__close }; + } + bpf_object__for_each_program(prog, obj) + { + // Link hasn't been used + bpf_program__attach(prog); + if (err) { + fprintf(stderr, "Error attaching BPF program\n"); + return { nullptr, bpf_object__close }; + } + } + return obj_ptr; +} + +int inject_agent(int target_pid, const char *agent_dynlib_path) +{ + // check the args + frida_init(); + FridaInjector *injector = frida_injector_new(); + GError *error = NULL; + guint id = frida_injector_inject_library_file_sync(injector, target_pid, + agent_dynlib_path, + "bpftime_agent_main", + "", NULL, &error); + + if (error != NULL) { + fprintf(stderr, "%s\n", error->message); + g_error_free(error); + frida_unref(injector); + frida_deinit(); + exit(EXIT_FAILURE); + } + + printf("Successfully injected. ID: %u\n", id); + + frida_injector_close_sync(injector, NULL, NULL); + frida_unref(injector); + frida_deinit(); + return 0; +} + +std::string get_lib_path(const char *library_name) +{ + struct stat st; + auto so_path = + std::string(DEFAULT_INSTALLATION_LOCATION) + library_name; + if (stat(so_path.c_str(), &st) != 0) { + cerr << "Error: necessary library " << so_path + << " not found:" << errno << endl; + exit(1); + } + return so_path; +} + +std::string get_agent_lib_path() +{ + return get_lib_path("/libbpftime-agent.so"); +} + +std::string get_server_lib_path() +{ + return get_lib_path("/libbpftime-syscall-server.so"); +} + +// Main program +int main(int argc, char *argv[]) +{ + if (argc == 1) { + cerr << "Usage: " << argv[0] << " [load|start|attach] ..." + << endl; + return 1; + } + + auto cmd = std::string(argv[1]); + if (cmd == "load") { + if (argc != 3) { + cerr << "Usage: " << argv[0] << " load " + << endl; + return 1; + } + auto so_path = get_server_lib_path(); + auto command_to_run = + "LD_PRELOAD=" + so_path + " " + std::string(argv[2]); + return system(command_to_run.c_str()); + } else if (cmd == "start") { + if (argc != 3) { + cerr << "Usage: " << argv[0] << " start " + << endl; + return 1; + } + auto so_path = get_agent_lib_path(); + auto command_to_run = + "LD_PRELOAD=" + so_path + " " + std::string(argv[2]); + return system(command_to_run.c_str()); + } else if (cmd == "attach") { + if (argc != 3) { + cerr << "Usage: " << argv[0] << " attach " << endl; + return 1; + } + // convert pid to int + int pid = atoi(argv[2]); + auto so_path = get_agent_lib_path(); + return inject_agent(pid, so_path.c_str()); + } else { + cerr << "Invalid subcommand " << cmd << endl; + return 1; + } + return EXIT_SUCCESS; +} \ No newline at end of file