diff --git a/.github/workflows/test-examples.yml b/.github/workflows/test-examples.yml index ced6d52e..ec4e0e29 100644 --- a/.github/workflows/test-examples.yml +++ b/.github/workflows/test-examples.yml @@ -31,11 +31,19 @@ jobs: - name: Build and install runtime (with llvm-jit) if: ${{matrix.enable_jit}} run: | - make release-with-llvm-jit -j + cmake -Bbuild -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo \ + -DBPFTIME_LLVM_JIT=1 \ + -DBUILD_BPFTIME_DAEMON=1 \ + -DCMAKE_CXX_FLAGS="-DDEFAULT_LOGGER_OUTPUT_PATH='\"console\"'" + cmake --build build --config RelWithDebInfo --target install -j - name: Build and install runtime (without llvm-jit) if: ${{!matrix.enable_jit}} run: | - make release -j + cmake -Bbuild -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo \ + -DBPFTIME_LLVM_JIT=0 \ + -DBUILD_BPFTIME_DAEMON=1 \ + -DCMAKE_CXX_FLAGS="-DDEFAULT_LOGGER_OUTPUT_PATH='\"console\"'" + cmake --build build --config RelWithDebInfo --target install -j - name: Upload build results (without jit) uses: actions/upload-artifact@v3 if: ${{!matrix.enable_jit}} diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c22e23b..8354529f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,7 +86,7 @@ message(STATUS "Started CMake for ${PROJECT_NAME} v${PROJECT_VERSION}...\n") # if option to build without libbpf is set if(${BPFTIME_BUILD_WITH_LIBBPF}) - add_definitions(-DUSE_LIBBPF) + add_definitions(-DBPFTIME_BUILD_WITH_LIBBPF=1) endif() if(UNIX) @@ -135,7 +135,7 @@ endif() add_subdirectory(third_party/spdlog) if(NOT DEFINED SPDLOG_ACTIVE_LEVEL) if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_compile_definitions(SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG) + add_compile_definitions(SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) else() add_compile_definitions(SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO) endif() diff --git a/README.md b/README.md index 76b7c613..a03479b4 100644 --- a/README.md +++ b/README.md @@ -17,18 +17,18 @@ ## Key Features -- **Uprobe and Syscall hooks based on binary rewriting**: Run eBPF programs in userspace, attaching them to Uprobes and Syscall tracepoints: **No manual instrumentation or restart required!**. It can `trace` or `change` the execution of a function, `hook` or `filter` all syscalls of a process safely, and efficiently with an eBPF userspace runtime. -- **Performance**: Experience up to a `10x` speedup in Uprobe overhead compared to kernel uprobe and uretprobe. +- **Uprobe and Syscall hooks based on binary rewriting**: Run eBPF programs in userspace, attaching them to Uprobes and Syscall tracepoints: **No manual instrumentation or restart required!**. It can `trace` or `change` the execution of a function, `hook` or `filter` all syscalls of a process safely, and efficiently with an eBPF userspace runtime. Can inject eBPF runtime into any running process without the need for a restart or manual recompilation. +- **Performance**: Experience up to a `10x` speedup in Uprobe overhead compared to kernel uprobe and uretprobe. Read/Write userspace memory is also faster than kernel eBPF. - **Interprocess eBPF Maps**: Implement userspace `eBPF maps` in shared userspace memory for summary aggregation or control plane communication. -- **Compatibility**: use `existing eBPF toolchains` like clang and libbpf to develop userspace eBPF without any modifications. Supporting CO-RE via BTF, and offering userspace host function access. -- **JIT Support**: Benefit from a cross-platform eBPF interpreter and a high-speed `JIT/AOT` compiler powered by LLVM. It also includes a handcrafted x86 JIT in C for limited resources. The vm can be built as `a standalone library` like ubpf. -- **No instrumentation**: Can inject eBPF runtime into any running process without the need for a restart or manual recompilation. +- **Compatibility**: use `existing eBPF toolchains` like clang, libbpf and bpftrace to develop userspace eBPF application without any modifications. Supporting CO-RE via BTF, and offering userspace `ufunc` access. +- **Multi JIT Support**: Support [llvmbpf](https://github.com/eunomia-bpf/llvmbpf), a high-speed `JIT/AOT` compiler powered by LLVM, or using `ubpf JIT` and INTERPRETER. The vm can be built as `a standalone library` like ubpf. - **Run with kernel eBPF**: Can load userspace eBPF from kernel, and using kernel eBPF maps to cooperate with kernel eBPF programs like kprobes and network filters. ## Components -- [`vm`](https://github.com/eunomia-bpf/bpftime/tree/master/vm): The eBPF VM and JIT for eBPF, you can choose from bpftime LLVM JIT and a simple JIT/interpreter based on ubpf. It can be built as a standalone library and integrated into other projects. The API is similar to ubpf. -- [`runtime`](https://github.com/eunomia-bpf/bpftime/tree/master/runtime): The userspace runtime for eBPF, including the syscall server and agent, attaching eBPF programs to Uprobes and Syscall tracepoints, and eBPF maps in shared memory. +- [`vm`](https://github.com/eunomia-bpf/bpftime/tree/master/vm): The eBPF VM and JIT compiler for bpftime, you can choose from [bpftime LLVM JIT/AOT compiler](https://github.com/eunomia-bpf/llvmbpf) and [ubpf](https://github.com/iovisor/ubpf). The [llvm-based vm](https://github.com/eunomia-bpf/llvmbpf) in bpftime can also be built as a standalone library and integrated into other projects, similar to ubpf. +- [`runtime`](https://github.com/eunomia-bpf/bpftime/tree/master/runtime): The userspace runtime for eBPF, including the bpf-syscall loader(`syscall-server`) and agent, support attaching eBPF programs to Uprobes, Syscall tracepoints and other events, as well as eBPF maps in shared memory. +- [verifier](https://github.com/eunomia-bpf/bpftime/tree/master/bpftime-verifier): Support using [PREVAIL](https://github.com/vbpf/ebpf-verifier) as userspace verifier, or using Linux kernel verifier as an option. - [`daemon`](https://github.com/eunomia-bpf/bpftime/tree/master/daemon): A daemon to make userspace eBPF working with kernel and compatible with kernel uprobe. Monitor and modify kernel eBPF events and syscalls, load eBPF in userspace from kernel. ## Quick Start diff --git a/attach/text_segment_transformer/agent-transformer.cpp b/attach/text_segment_transformer/agent-transformer.cpp index c75684e6..049c04c8 100644 --- a/attach/text_segment_transformer/agent-transformer.cpp +++ b/attach/text_segment_transformer/agent-transformer.cpp @@ -69,7 +69,7 @@ extern "C" void bpftime_agent_main(const gchar *data, gboolean *stay_resident) "Please set AGENT_SO to the bpftime-agent when use this tranformer"); return; } - SPDLOG_INFO("Using agent {}", agent_so); + SPDLOG_DEBUG("Using agent {}", agent_so); cs_arch_register_x86(); bpftime::setup_syscall_tracer(); SPDLOG_DEBUG("Loading dynamic library.."); @@ -98,5 +98,5 @@ extern "C" void bpftime_agent_main(const gchar *data, gboolean *stay_resident) bpftime::get_call_hook(); entry_func(&orig_syscall_hooker_func); bpftime::set_call_hook(orig_syscall_hooker_func); - SPDLOG_INFO("Transformer exiting, trace will be usable now"); + SPDLOG_DEBUG("Transformer exiting, syscall trace is usable now"); } diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 4b131af9..fca3324f 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -78,7 +78,7 @@ set(sources extension/extension_helper.cpp ) -if(UNIX AND NOT APPLE) +if(UNIX AND NOT APPLE AND BPFTIME_BUILD_WITH_LIBBPF) list(APPEND sources src/bpf_map/shared/array_map_kernel_user.cpp src/bpf_map/shared/hash_map_kernel_user.cpp @@ -94,13 +94,6 @@ set(headers message(INFO " Headers: ${headers}") message(INFO " Found the following sources: ${sources}") - -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG) -else() - add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO) -endif() - add_library( ${PROJECT_NAME} ${sources} @@ -238,16 +231,17 @@ message(DEBUG "Successfully added all dependencies and linked against them.") set(BPFTIME_RUNTIME_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src) - +if (BPFTIME_BUILD_WITH_LIBBPF) add_subdirectory(object) +endif() add_subdirectory(agent) add_subdirectory(syscall-server) # # Unit testing setup # -if(BPFTIME_ENABLE_UNIT_TESTING) +if(BPFTIME_ENABLE_UNIT_TESTING AND BPFTIME_BUILD_WITH_LIBBPF) enable_testing() - message(STATUS "Build unit tests for the project. Tests should always be found in the test folder\n") + message(STATUS "Build unit tests for the runtime. Tests should always be found in the test folder\n") add_subdirectory(test) add_subdirectory(unit-test) endif() diff --git a/runtime/agent/agent.cpp b/runtime/agent/agent.cpp index 6dda93f8..e6103835 100644 --- a/runtime/agent/agent.cpp +++ b/runtime/agent/agent.cpp @@ -6,6 +6,7 @@ #include "spdlog/common.h" #include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/sinks/stdout_sinks.h" +#include "bpftime_logger.hpp" #include #include #include @@ -21,10 +22,11 @@ #include "bpftime_shm.hpp" #include #include -#if __linux__ +#if __linux__ && BPFTIME_BUILD_WITH_LIBBPF #include "syscall_trace_attach_impl.hpp" #include "syscall_trace_attach_private_data.hpp" #endif + using namespace bpftime; using namespace bpftime::attach; using main_func_t = int (*)(int, char **, char **); @@ -94,18 +96,17 @@ static void sig_handler_sigusr1(int sig) shm_holder.global_shared_memory.remove_pid_from_alive_agent_set( getpid()); SPDLOG_DEBUG("Detaching done"); + bpftime_logger_flush(); } extern "C" void bpftime_agent_main(const gchar *data, gboolean *stay_resident) { - auto logger = spdlog::stderr_color_mt("stderr"); - logger->set_pattern("[%Y-%m-%d %H:%M:%S][%^%l%$][%t] %v"); - spdlog::set_default_logger(logger); + auto runtime_config = bpftime_get_agent_config(); + bpftime_set_logger(runtime_config.logger_output_path); SPDLOG_DEBUG("Entered bpftime_agent_main"); SPDLOG_DEBUG("Registering signal handler"); // We use SIGUSR1 to indicate the detaching signal(SIGUSR1, sig_handler_sigusr1); - spdlog::cfg::load_env_levels(); try { // If we are unable to initialize shared memory.. bpftime_initialize_global_shm(shm_open_type::SHM_OPEN_ONLY); @@ -121,7 +122,7 @@ extern "C" void bpftime_agent_main(const gchar *data, gboolean *stay_resident) getpid()); } ctx_holder.init(); - #if __linux__ + #if __linux__ && BPFTIME_BUILD_WITH_LIBBPF // Register syscall trace impl auto syscall_trace_impl = std::make_unique(); syscall_trace_impl->set_original_syscall_function(orig_hooker); @@ -164,7 +165,7 @@ extern "C" void bpftime_agent_main(const gchar *data, gboolean *stay_resident) SPDLOG_DEBUG("Set environment variable BPFTIME_USED"); try { res = ctx_holder.ctx.init_attach_ctx_from_handlers( - bpftime_get_agent_config()); + runtime_config); if (res != 0) { SPDLOG_INFO("Failed to initialize attach context, exiting.."); return; @@ -178,7 +179,7 @@ extern "C" void bpftime_agent_main(const gchar *data, gboolean *stay_resident) // using definition for libbpf for syscall issues // maybe should separate libbpf and kernel features separately -#if __linux__ +#if __linux__ && BPFTIME_BUILD_WITH_LIBBPF extern "C" int64_t syscall_callback(int64_t sys_nr, int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg5, int64_t arg6) diff --git a/runtime/include/bpftime_config.hpp b/runtime/include/bpftime_config.hpp index 2ba0e334..d899fbc8 100644 --- a/runtime/include/bpftime_config.hpp +++ b/runtime/include/bpftime_config.hpp @@ -4,6 +4,11 @@ #include #include +#ifndef DEFAULT_LOGGER_OUTPUT_PATH +#define DEFAULT_LOGGER_OUTPUT_PATH "~/.bpftime/runtime.log" +#endif +#define stringize(x) #x + namespace bpftime { // Configuration for the bpftime runtime @@ -28,10 +33,16 @@ struct agent_config { // available for the eBPF programs and maps // The value is in MB int shm_memory_size = 20; // 20MB + + // specify the where the logger output should be written to + // It can be a file path or "console". + // If it is "console", the logger will output to stderr + std::string logger_output_path = DEFAULT_LOGGER_OUTPUT_PATH; }; // Get the bpftime configuration from the environment variables -const agent_config get_agent_config_from_env(); +// If the shared memory is not int, this should be called first +const agent_config get_agent_config_from_env() noexcept; } // namespace bpftime diff --git a/runtime/include/bpftime_logger.hpp b/runtime/include/bpftime_logger.hpp new file mode 100644 index 00000000..9364687a --- /dev/null +++ b/runtime/include/bpftime_logger.hpp @@ -0,0 +1,73 @@ +#include +#include "spdlog/spdlog.h" +#include "spdlog/cfg/env.h" +#include "spdlog/sinks/rotating_file_sink.h" +#include "spdlog/sinks/stdout_color_sinks.h" +#include +#include +#include +#include + +namespace bpftime +{ + +inline std::string expand_user_path(const std::string &input_path) +{ + if (input_path.empty()) { + return "console"; + } + + if (input_path[0] == '~') { + const char *homeDir = getenv("HOME"); + if (!homeDir) { + return "console"; + } + + if (input_path.size() == 1 || input_path[1] == '/') { + // Replace "~" with the home directory + std::string expandedPath = homeDir + + input_path.substr(1); + return expandedPath; + } else { + return "console"; // Unsupported path format + } + } + + // Return the original path if no tilde expansion is needed + return input_path; +} + +inline void bpftime_set_logger(const std::string &target) noexcept +{ + std::string logger_target = expand_user_path(target); + + if (logger_target == "console") { + // Set logger to stderr + auto logger = spdlog::stderr_color_mt("stderr"); + logger->set_pattern("[%Y-%m-%d %H:%M:%S][%^%l%$][%t] %v"); + logger->flush_on(spdlog::level::info); + spdlog::set_default_logger(logger); + } else { + // Set logger to file, with rotation 5MB and 3 files + auto max_size = 1048576 * 5; + auto max_files = 3; + auto logger = spdlog::rotating_logger_mt( + "bpftime_logger", logger_target, max_size, max_files); + logger->set_pattern("[%Y-%m-%d %H:%M:%S][%^%l%$][%t] %v"); + logger->flush_on(spdlog::level::info); + spdlog::set_default_logger(logger); + } + + // Load log level from environment + spdlog::cfg::load_env_levels(); +} + +/* + Flush the logger. +*/ +inline void bpftime_logger_flush() +{ + spdlog::default_logger()->flush(); +} + +} // namespace bpftime diff --git a/runtime/include/bpftime_shm.hpp b/runtime/include/bpftime_shm.hpp index 1d062f6c..47642ad5 100644 --- a/runtime/include/bpftime_shm.hpp +++ b/runtime/include/bpftime_shm.hpp @@ -10,13 +10,16 @@ #include #include #include -#include #if __linux__ #include #elif __APPLE__ #include "bpftime_epoll.h" #endif +extern "C" { +struct ebpf_inst; +} + namespace bpftime { @@ -153,7 +156,13 @@ enum class bpf_prog_type { }; extern const shm_open_type global_shm_open_type; + +// Get the runtime config in the shared memory. +// The shared memory should be initialized before calling this function. +// This should be called by the agent side instead of the server side. const bpftime::agent_config &bpftime_get_agent_config(); + +// Set the runtime config in the shared memory. void bpftime_set_agent_config(const bpftime::agent_config &cfg); // Map ops for register external map types and operations @@ -272,7 +281,6 @@ int bpftime_link_create(int fd, struct bpf_link_create_args *args); int bpftime_progs_create(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 @@ -314,13 +322,17 @@ int bpftime_perf_event_enable(int fd); // disable the perf event int bpftime_perf_event_disable(int fd); +// find the minimal unused fd in shared memory +// Which is a available handler for bpf related object int bpftime_find_minimal_unused_fd(); int bpftime_attach_perf_to_bpf(int perf_fd, int bpf_fd); int bpftime_attach_perf_to_bpf_with_cookie(int perf_fd, int bpf_fd, uint64_t cookie); + 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); @@ -328,6 +340,9 @@ int bpftime_epoll_create(); void *bpftime_get_ringbuf_consumer_page(int ringbuf_fd); void *bpftime_get_ringbuf_producer_page(int ringbuf_fd); +int bpftime_poll_from_ringbuf(int rb_fd, void *ctx, + int (*cb)(void *, void *, size_t)); + int bpftime_is_ringbuf_map(int fd); int bpftime_is_array_map(int fd); int bpftime_is_epoll_handler(int fd); @@ -350,7 +365,7 @@ int bpftime_add_software_perf_event(int cpu, int32_t sample_type, 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); -#if __linux__ +#if __linux__ && BPFTIME_BUILD_WITH_LIBBPF int bpftime_shared_perf_event_output(int map_fd, const void *buf, size_t sz); #endif int bpftime_add_ureplace_or_override(int fd, int pid, const char *name, @@ -359,9 +374,6 @@ int bpftime_add_ureplace_or_override(int fd, int pid, const char *name, int bpftime_get_current_thread_cookie(uint64_t *out); int bpftime_add_custom_perf_event(int type, const char *attach_argument); - -int bpftime_poll_from_ringbuf(int rb_fd, void *ctx, - int (*cb)(void *, void *, size_t)); } #endif // BPFTIME_SHM_CPP_H diff --git a/runtime/object/bpf_object.cpp b/runtime/object/bpf_object.cpp index 7357fa6f..95ffe8b1 100644 --- a/runtime/object/bpf_object.cpp +++ b/runtime/object/bpf_object.cpp @@ -15,7 +15,7 @@ using namespace bpftime; using namespace bpftime_epoll; #endif -#ifdef USE_LIBBPF +#ifdef BPFTIME_BUILD_WITH_LIBBPF extern "C" { #include #include diff --git a/runtime/src/bpf_helper.cpp b/runtime/src/bpf_helper.cpp index 29675031..981cddaf 100644 --- a/runtime/src/bpf_helper.cpp +++ b/runtime/src/bpf_helper.cpp @@ -7,10 +7,10 @@ #include #include #endif -#ifdef USE_LIBBPF +#ifdef BPFTIME_BUILD_WITH_LIBBPF #include "bpf/bpf.h" #include "bpf/libbpf_common.h" -#endif +#endif #include "bpftime_helper_group.hpp" #include #ifdef ENABLE_BPFTIME_VERIFIER @@ -89,11 +89,11 @@ uint64_t bpftime_ktime_get_coarse_ns(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) { timespec spec; - #ifdef __APPLE__ +#ifdef __APPLE__ clock_gettime(CLOCK_MONOTONIC, &spec); // or CLOCK_MONOTONIC_RAW - #else +#else clock_gettime(CLOCK_MONOTONIC_COARSE, &spec); - #endif +#endif return spec.tv_sec * (uint64_t)1000000000 + spec.tv_nsec; } @@ -101,19 +101,19 @@ uint64_t bpftime_get_current_pid_tgid(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) { static int tgid = getpid(); - #if __linux__ +#if __linux__ static thread_local int tid = -1; - if(tid == -1) - { + if (tid == -1) { tid = gettid(); } - #elif __APPLE__ - static thread_local uint64_t tid = UINT64_MAX; //cannot use int because pthread_threadid_np expects only uint64_t - if (tid == UINT64_MAX) - { - pthread_threadid_np(NULL, &tid); +#elif __APPLE__ + static thread_local uint64_t tid = UINT64_MAX; // cannot use int because + // pthread_threadid_np + // expects only uint64_t + if (tid == UINT64_MAX) { + pthread_threadid_np(NULL, &tid); } - #endif +#endif return ((uint64_t)tgid << 32) | tid; } @@ -196,11 +196,11 @@ uint64_t bpf_ktime_get_coarse_ns(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) { struct timespec ts; - #if __APPLE__ +#if __APPLE__ clock_gettime(CLOCK_MONOTONIC, &ts); // or CLOCK_MONOTONIC_RAW - #else +#else clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); - #endif +#endif return (uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec; } @@ -303,15 +303,14 @@ uint64_t bpf_perf_event_output(uint64_t ctx, uint64_t map, uint64_t flags, ret = bpftime_perf_event_output(perf_handler_fd, (const void *)(uintptr_t)data, (size_t)size); - } - #if __linux__ - else if (map_ty == - bpftime::bpf_map_type:: - BPF_MAP_TYPE_KERNEL_USER_PERF_EVENT_ARRAY) { + } +#if __linux__ && BPFTIME_BUILD_WITH_LIBBPF + else if (map_ty == bpftime::bpf_map_type:: + BPF_MAP_TYPE_KERNEL_USER_PERF_EVENT_ARRAY) { ret = bpftime_shared_perf_event_output( fd, (const void *)(uintptr_t)data, (size_t)size); - } - #endif + } +#endif else { SPDLOG_ERROR( "Attempting to run perf_output on a non-perf array map"); @@ -321,8 +320,10 @@ uint64_t bpf_perf_event_output(uint64_t ctx, uint64_t map, uint64_t flags, sched_setaffinity(0, sizeof(orig), &orig); return (uint64_t)ret; } + uint64_t bpftime_tail_call(uint64_t ctx, uint64_t prog_array, uint64_t index) { +#ifdef BPFTIME_BUILD_WITH_LIBBPF int fd = prog_array >> 32; if (!bpftime_is_prog_array(fd)) { SPDLOG_ERROR("Expected fd {} to be a prog array fd", fd); @@ -344,7 +345,6 @@ uint64_t bpftime_tail_call(uint64_t ctx, uint64_t prog_array, uint64_t index) } else { memset(context, 0, sizeof(context)); } - #ifdef USE_LIBBPF LIBBPF_OPTS(bpf_test_run_opts, run_opts, .ctx_in = context, // .ctx_out = context_out, .ctx_size_in = sizeof(context), @@ -358,7 +358,10 @@ uint64_t bpftime_tail_call(uint64_t ctx, uint64_t prog_array, uint64_t index) } close(to_call_fd); return run_opts.retval; - #endif +#else + SPDLOG_ERROR("tail_call is not supported in this build"); + return -ENOTSUP; +#endif } uint64_t bpftime_get_attach_cookie(uint64_t ctx, uint64_t, uint64_t, uint64_t, diff --git a/runtime/src/bpftime_config.cpp b/runtime/src/bpftime_config.cpp index 2b829ba3..cd313142 100644 --- a/runtime/src/bpftime_config.cpp +++ b/runtime/src/bpftime_config.cpp @@ -1,6 +1,7 @@ #include "bpftime_config.hpp" #include "spdlog/spdlog.h" #include +#include using namespace bpftime; @@ -38,7 +39,7 @@ static void process_helper_sv(const std::string_view &str, const char delimiter, } } -const agent_config bpftime::get_agent_config_from_env() +const agent_config bpftime::get_agent_config_from_env() noexcept { bpftime::agent_config agent_config; if (const char *custom_helpers = getenv("BPFTIME_HELPER_GROUPS"); @@ -50,18 +51,19 @@ const agent_config bpftime::get_agent_config_from_env() auto helpers_sv = std::string_view(custom_helpers); process_helper_sv(helpers_sv, ',', agent_config); } else { - SPDLOG_INFO( - "Enabling helper groups ufunc, kernel, shm_map by default"); agent_config.enable_kernel_helper_group = agent_config.enable_shm_maps_helper_group = agent_config.enable_ufunc_helper_group = true; } + if (getenv("BPFTIME_DISABLE_JIT") != nullptr) { agent_config.jit_enabled = false; } + if (getenv("BPFTIME_ALLOW_EXTERNAL_MAPS") != nullptr) { agent_config.allow_non_buildin_map_types = true; } + const char *shm_memory_size_str = getenv("BPFTIME_SHM_MEMORY_MB"); if (shm_memory_size_str != nullptr) { try { @@ -73,5 +75,10 @@ const agent_config bpftime::get_agent_config_from_env() shm_memory_size_str); } } + + const char *logger_target = std::getenv("BPFTIME_LOG_OUTPUT"); + if (logger_target != NULL) { + agent_config.logger_output_path = logger_target; + } return agent_config; } diff --git a/runtime/src/bpftime_shm.cpp b/runtime/src/bpftime_shm.cpp index 049b83ae..1e1e82c3 100644 --- a/runtime/src/bpftime_shm.cpp +++ b/runtime/src/bpftime_shm.cpp @@ -516,7 +516,7 @@ int bpftime_perf_event_output(int fd, const void *buf, size_t sz) } } -#if __linux__ +#if __linux__ && BPFTIME_BUILD_WITH_LIBBPF int bpftime_shared_perf_event_output(int map_fd, const void *buf, size_t sz) { SPDLOG_DEBUG("Output data into shared perf event array fd {}", map_fd); diff --git a/runtime/src/bpftime_shm_internal.cpp b/runtime/src/bpftime_shm_internal.cpp index 35dbfc6c..4b8bf604 100644 --- a/runtime/src/bpftime_shm_internal.cpp +++ b/runtime/src/bpftime_shm_internal.cpp @@ -4,6 +4,7 @@ * All rights reserved. */ #include "bpftime_shm.hpp" +#include #include "handler/epoll_handler.hpp" #include "handler/handler_manager.hpp" #include "handler/link_handler.hpp" @@ -41,8 +42,10 @@ extern "C" void bpftime_destroy_global_shm() shm_holder.global_shared_memory.~bpftime_shm(); // Why not spdlog? because global variables that spdlog used // were already destroyed.. +#ifdef DEBUG fprintf(stderr, "INFO [%d]: Global shm destructed\n", (int)getpid()); +#endif } } @@ -736,7 +739,7 @@ void bpftime_shm::set_agent_config(const struct agent_config &config) const struct agent_config &bpftime_shm::get_agent_config() { if (agent_config == nullptr) { - SPDLOG_INFO("use current process config"); + SPDLOG_DEBUG("use current process config"); return local_agent_config; } return *agent_config; diff --git a/runtime/src/bpftime_shm_json.cpp b/runtime/src/bpftime_shm_json.cpp index 2c27fb18..de21d657 100644 --- a/runtime/src/bpftime_shm_json.cpp +++ b/runtime/src/bpftime_shm_json.cpp @@ -4,6 +4,7 @@ * All rights reserved. */ #include "bpftime_shm.hpp" +#include #include "handler/epoll_handler.hpp" #include "handler/perf_event_handler.hpp" #include "spdlog/spdlog.h" diff --git a/runtime/src/handler/map_handler.cpp b/runtime/src/handler/map_handler.cpp index 417eb8c6..8224d2fe 100644 --- a/runtime/src/handler/map_handler.cpp +++ b/runtime/src/handler/map_handler.cpp @@ -12,7 +12,7 @@ #include #include #include -#ifdef USE_LIBBPF +#ifdef BPFTIME_BUILD_WITH_LIBBPF #include #include #include @@ -119,7 +119,7 @@ const void *bpf_map_handler::map_lookup_elem(const void *key, return from_syscall ? do_lookup_userspace(impl) : do_lookup(impl); } - #ifdef USE_LIBBPF + #ifdef BPFTIME_BUILD_WITH_LIBBPF case bpf_map_type::BPF_MAP_TYPE_KERNEL_USER_ARRAY: { auto impl = static_cast( map_impl_ptr.get()); @@ -213,7 +213,7 @@ long bpf_map_handler::map_update_elem(const void *key, const void *value, return from_syscall ? do_update_userspace(impl) : do_update(impl); } - #ifdef USE_LIBBPF + #ifdef BPFTIME_BUILD_WITH_LIBBPF case bpf_map_type::BPF_MAP_TYPE_KERNEL_USER_ARRAY: { auto impl = static_cast( map_impl_ptr.get()); @@ -300,7 +300,7 @@ int bpf_map_handler::bpf_map_get_next_key(const void *key, void *next_key, map_impl_ptr.get()); return do_get_next_key(impl); } - #if __linux__ + #if __linux__ && defined(BPFTIME_BUILD_WITH_LIBBPF) case bpf_map_type::BPF_MAP_TYPE_KERNEL_USER_ARRAY: { auto impl = static_cast( map_impl_ptr.get()); @@ -394,7 +394,7 @@ long bpf_map_handler::map_delete_elem(const void *key, bool from_syscall) const return from_syscall ? do_delete_userspace(impl) : do_delete(impl); } - #ifdef USE_LIBBPF + #ifdef BPFTIME_BUILD_WITH_LIBBPF case bpf_map_type::BPF_MAP_TYPE_KERNEL_USER_ARRAY: { auto impl = static_cast( map_impl_ptr.get()); @@ -491,7 +491,7 @@ int bpf_map_handler::map_init(managed_shared_memory &memory) container_name.c_str())(memory, key_size, value_size); return 0; } - #ifdef USE_LIBBPF + #ifdef BPFTIME_BUILD_WITH_LIBBPF case bpf_map_type::BPF_MAP_TYPE_KERNEL_USER_ARRAY: { map_impl_ptr = memory.construct( container_name.c_str())(memory, attr.kernel_bpf_map_id); @@ -570,7 +570,7 @@ void bpf_map_handler::map_free(managed_shared_memory &memory) case bpf_map_type::BPF_MAP_TYPE_PERCPU_HASH: memory.destroy(container_name.c_str()); break; - #ifdef USE_LIBBPF + #ifdef BPFTIME_BUILD_WITH_LIBBPF case bpf_map_type::BPF_MAP_TYPE_KERNEL_USER_ARRAY: memory.destroy( container_name.c_str()); diff --git a/runtime/syscall-server/CMakeLists.txt b/runtime/syscall-server/CMakeLists.txt index 77f8e5f8..6df0b224 100644 --- a/runtime/syscall-server/CMakeLists.txt +++ b/runtime/syscall-server/CMakeLists.txt @@ -20,9 +20,11 @@ target_link_libraries(bpftime-syscall-server PUBLIC add_dependencies(bpftime-syscall-server spdlog::spdlog) if(${BPFTIME_BUILD_WITH_LIBBPF}) + target_include_directories(bpftime-syscall-server PUBLIC - "../../core" + "../include" + "../../vm" "../../third_party/libbpf/include" "../../third_party/libbpf/include/uapi" ${SPDLOG_INCLUDE} @@ -30,12 +32,13 @@ target_include_directories(bpftime-syscall-server else() target_include_directories(bpftime-syscall-server PUBLIC - "../../core" + "../../vm" ${SPDLOG_INCLUDE} ) endif() set_target_properties(bpftime-syscall-server PROPERTIES CXX_STANDARD 20 LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/syscall-server.version) + if(UNIX AND NOT APPLE) target_link_options(bpftime-syscall-server PRIVATE -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/syscall-server.version) endif() diff --git a/runtime/syscall-server/syscall_context.cpp b/runtime/syscall-server/syscall_context.cpp index 46285a28..252b3b09 100644 --- a/runtime/syscall-server/syscall_context.cpp +++ b/runtime/syscall-server/syscall_context.cpp @@ -3,7 +3,9 @@ * Copyright (c) 2022, eunomia-bpf org * All rights reserved. */ +#include "bpftime_logger.hpp" #include "bpftime_shm.hpp" +#include #include "syscall_context.hpp" #include "handler/perf_event_handler.hpp" #if __linux__ @@ -15,7 +17,7 @@ #include #elif __APPLE__ #include "bpftime_epoll.h" -#endif +#endif #include "spdlog/spdlog.h" #include #include @@ -25,6 +27,25 @@ #include #include +// In build option without libbpf, there might be no BPF_EXIT_INSN +#ifndef BPF_EXIT_INSN +#define BPF_EXIT_INSN() \ + ((struct bpf_insn){ .code = BPF_JMP | BPF_EXIT, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = 0 }) +#endif + +#ifndef BPF_MOV64_IMM +#define BPF_MOV64_IMM(DST, IMM) \ + ((struct bpf_insn){ .code = BPF_ALU64 | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) +#endif + using namespace bpftime; #if __APPLE__ using namespace bpftime_epoll; @@ -49,6 +70,17 @@ void syscall_context::load_config_from_env() } } +syscall_context::syscall_context() +{ + init_original_functions(); + // FIXME: merge this into the runtime config + load_config_from_env(); + auto runtime_config = bpftime::get_agent_config_from_env(); + SPDLOG_INFO("Init bpftime syscall mocking.."); + SPDLOG_INFO("The log will be written to: {}", + runtime_config.logger_output_path); +} + void syscall_context::try_startup() { enable_mock = false; diff --git a/runtime/syscall-server/syscall_context.hpp b/runtime/syscall-server/syscall_context.hpp index 74e1d767..37e07baf 100644 --- a/runtime/syscall-server/syscall_context.hpp +++ b/runtime/syscall-server/syscall_context.hpp @@ -75,23 +75,20 @@ class syscall_context { // if the syscall original function is not prepared, it will cause a // segfault. void try_startup(); + // enable userspace eBPF runing with kernel eBPF. bool run_with_kernel = false; // allow programs to by pass the verifier // some extensions are not supported by the verifier, so we need to // by pass the verifier to make it work. std::string by_pass_kernel_verifier_pattern; + void load_config_from_env(); public: // enable mock the syscall behavior in userspace bool enable_mock = true; - syscall_context() - { - init_original_functions(); - load_config_from_env(); - SPDLOG_INFO("manager constructed"); - } + syscall_context(); syscall_fn orig_syscall_fn = nullptr; // handle syscall diff --git a/runtime/syscall-server/syscall_server_main.cpp b/runtime/syscall-server/syscall_server_main.cpp index 0a8dfb7b..244100c5 100644 --- a/runtime/syscall-server/syscall_server_main.cpp +++ b/runtime/syscall-server/syscall_server_main.cpp @@ -31,7 +31,7 @@ auto handle_exceptions(F &&f, Args &&...args) noexcept SPDLOG_ERROR("Boost interprocess bad_alloc: {}", e.what()); SPDLOG_ERROR("Consider increasing the shared memory size by " "setting the BPFTIME_SHM_MEMORY_MB env var."); - std::abort(); + std::exit(1); // Terminate the program after logging the exception } // More exceptions can be added here diff --git a/runtime/syscall-server/syscall_server_utils.cpp b/runtime/syscall-server/syscall_server_utils.cpp index 09d78b5f..e783e588 100644 --- a/runtime/syscall-server/syscall_server_utils.cpp +++ b/runtime/syscall-server/syscall_server_utils.cpp @@ -6,6 +6,7 @@ #include "bpftime_shm_internal.hpp" #include #include +#include "bpftime_logger.hpp" #include #ifdef ENABLE_BPFTIME_VERIFIER #include @@ -21,11 +22,11 @@ void start_up() if (already_setup) return; already_setup = true; + const auto agent_config = get_agent_config_from_env(); + bpftime_set_logger(agent_config.logger_output_path); SPDLOG_INFO("Initialize syscall server"); - spdlog::cfg::load_env_levels(); - spdlog::set_pattern("[%Y-%m-%d %H:%M:%S][%^%l%$][%t] %v"); + bpftime_initialize_global_shm(shm_open_type::SHM_REMOVE_AND_CREATE); - const auto agent_config = get_agent_config_from_env(); bpftime_set_agent_config(agent_config); #ifdef ENABLE_BPFTIME_VERIFIER std::vector helper_ids; diff --git a/tools/cli/main.cpp b/tools/cli/main.cpp index 056674ca..c18fe0d8 100644 --- a/tools/cli/main.cpp +++ b/tools/cli/main.cpp @@ -1,7 +1,7 @@ #include "bpftime_shm.hpp" #include "bpftime_shm_internal.hpp" -#include "spdlog/spdlog.h" -#include "spdlog/cfg/env.h" +#include "bpftime_config.hpp" +#include "bpftime_logger.hpp" #include #include #include @@ -25,39 +25,46 @@ #include #include #include -inline char** get_environ() { - return *_NSGetEnviron(); +inline char **get_environ() +{ + return *_NSGetEnviron(); } -constexpr const char* AGENT_LIBRARY = "libbpftime-agent.dylib"; -constexpr const char* SYSCALL_SERVER_LIBRARY = "libbpftime-syscall-server.dylib"; -constexpr const char* AGENT_TRANSFORMER_LIBRARY = "libbpftime-agent-transformer.dylib"; -const char *strchrnul(const char *s, int c) { - while (*s && *s != (char)c) { - s++; - } - return s; +constexpr const char *AGENT_LIBRARY = "libbpftime-agent.dylib"; +constexpr const char *SYSCALL_SERVER_LIBRARY = + "libbpftime-syscall-server.dylib"; +constexpr const char *AGENT_TRANSFORMER_LIBRARY = + "libbpftime-agent-transformer.dylib"; +const char *strchrnul(const char *s, int c) +{ + while (*s && *s != (char)c) { + s++; + } + return s; } -int execvpe(const char *file, char *const argv[], char *const envp[]) { - for (const char *path = getenv("PATH"); path && *path; path = strchr(path, ':') + 1) { - char buf[PATH_MAX]; - const char *end = strchrnul(path, ':'); - size_t len = end - path; - memcpy(buf, path, len); - buf[len] = '/'; - strcpy(buf + len + 1, file); - execve(buf, argv, envp); - if (errno != ENOENT) - return -1; - } - errno = ENOENT; - return -1; +int execvpe(const char *file, char *const argv[], char *const envp[]) +{ + for (const char *path = getenv("PATH"); path && *path; + path = strchr(path, ':') + 1) { + char buf[PATH_MAX]; + const char *end = strchrnul(path, ':'); + size_t len = end - path; + memcpy(buf, path, len); + buf[len] = '/'; + strcpy(buf + len + 1, file); + execve(buf, argv, envp); + if (errno != ENOENT) + return -1; + } + errno = ENOENT; + return -1; } #elif __linux__ extern char **environ; -constexpr const char* AGENT_LIBRARY = "libbpftime-agent.so"; -constexpr const char* SYSCALL_SERVER_LIBRARY = "libbpftime-syscall-server.so"; -constexpr const char* AGENT_TRANSFORMER_LIBRARY = "libbpftime-agent-transformer.so"; -#else +constexpr const char *AGENT_LIBRARY = "libbpftime-agent.so"; +constexpr const char *SYSCALL_SERVER_LIBRARY = "libbpftime-syscall-server.so"; +constexpr const char *AGENT_TRANSFORMER_LIBRARY = + "libbpftime-agent-transformer.so"; +#else #error "Unsupported Platform" #endif @@ -83,11 +90,11 @@ static int run_command(const char *path, const std::vector &argv, agent_so_str += agent_so; } std::vector env_arr; - #if __APPLE__ +#if __APPLE__ char **p = get_environ(); - #else +#else char **p = environ; - #endif +#endif while (*p) { env_arr.push_back(*p); p++; @@ -180,7 +187,8 @@ static void signal_handler(int sig) int main(int argc, const char **argv) { - spdlog::cfg::load_env_levels(); + const auto agent_config = bpftime::get_agent_config_from_env(); + bpftime::bpftime_set_logger(agent_config.logger_output_path); signal(SIGINT, signal_handler); signal(SIGTSTP, signal_handler); argparse::ArgumentParser program(argv[0]);