diff --git a/.github/workflows/test-attach.yml b/.github/workflows/test-attach.yml index 4a556fc6..a925f767 100644 --- a/.github/workflows/test-attach.yml +++ b/.github/workflows/test-attach.yml @@ -26,24 +26,76 @@ jobs: - uses: actions/checkout@v2 with: submodules: 'recursive' - + - name: Install dependencies + if: "matrix.container == 'ubuntu-2204'" + run: | + apt-get install -y lcov libzstd-dev libboost-all-dev gpg + - name: Install lcov + if: "matrix.container == 'fedora-39'" + run: | + dnf install -y lcov + - name: Build for frida uprobe attach tests run: | - cmake -B build + cmake -DTEST_LCOV=ON -B build cmake --build build --config Debug --target bpftime_frida_uprobe_attach_tests -j$(nproc) - name: Run frida uprobe attach tests run: | ./build/attach/frida_uprobe_attach_impl/bpftime_frida_uprobe_attach_tests - + - name: Generate frida uprobe attach test coverage (Fedora) + if: "matrix.container == 'fedora-39'" + run: | + lcov --capture --directory . --output-file coverage-uprobe.info + lcov --remove coverage-uprobe.info '/usr/*' --output-file coverage-uprobe.info + lcov --list coverage-uprobe.info + - name: Generate frida uprobe attach test coverage (Ubuntu) + if: "matrix.container == 'ubuntu-2204'" + run: | + lcov --capture --directory . --output-file coverage-uprobe.info --gcov-tool $(which gcov-12) + lcov --remove coverage-uprobe.info '/usr/*' --output-file coverage-uprobe.info + lcov --list coverage-uprobe.info + - name: Upload uprobe coverage + uses: actions/upload-artifact@v4 + with: + name: coverage-uprobe-${{matrix.container}} + include-hidden-files: false + path: | + ./coverage-uprobe.info - name: Remove the build run: rm -rf build - name: Build syscall trace uprobe attach tests run: | - cmake -B build + cmake -DTEST_LCOV=ON -B build cmake --build build --config Debug --target bpftime_syscall_trace_attach_tests -j$(nproc) - name: Run syscall trace uprobe attach tests run: | ./build/attach/syscall_trace_attach_impl/bpftime_syscall_trace_attach_tests + - name: Generate syscall trace uprobe attach coverage (Ubuntu) + if: "matrix.container == 'ubuntu-2204'" + run: | + lcov --capture --directory . --output-file coverage-syscall-trace.info --gcov-tool $(which gcov-12) + lcov --remove coverage-syscall-trace.info '/usr/*' --output-file coverage-syscall-trace.info + lcov --list coverage-syscall-trace.info + - name: Generate syscall trace uprobe attach coverage (Fedora) + if: "matrix.container == 'fedora-39'" + run: | + lcov --capture --directory . --output-file coverage-syscall-trace.info + lcov --remove coverage-syscall-trace.info '/usr/*' --output-file coverage-syscall-trace.info + lcov --list coverage-syscall-trace.info + - name: Upload uprobe coverage + uses: actions/upload-artifact@v4 + with: + name: coverage-syscall-trace-${{matrix.container}} + include-hidden-files: false + path: | + ./coverage-syscall-trace.info + - uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true # optional (default = false) + files: ./coverage-syscall-trace.info, ./coverage-uprobe.info # optional + flags: attach tests (uprobe & syscall trace) + token: ${{ secrets.CODECOV_TOKEN }} # required + verbose: true # optional (default = false) diff --git a/.github/workflows/test-runtime.yml b/.github/workflows/test-runtime.yml index eafcf6a1..769d117c 100644 --- a/.github/workflows/test-runtime.yml +++ b/.github/workflows/test-runtime.yml @@ -24,19 +24,74 @@ jobs: - uses: actions/checkout@v2 with: submodules: 'recursive' + - name: Install lcov + if: "matrix.container == 'ubuntu-2204'" + run: | + apt-get install -y lcov libzstd-dev libboost-all-dev gpg + - name: Install lcov + if: "matrix.container == 'fedora-39'" + run: | + dnf install -y lcov + - name: Build run: | - cmake -DBPFTIME_LLVM_JIT=YES -DBPFTIME_ENABLE_UNIT_TESTING=YES -DCMAKE_BUILD_TYPE=Debug -B build + cmake -DTEST_LCOV=ON -DBPFTIME_LLVM_JIT=YES -DBPFTIME_ENABLE_UNIT_TESTING=YES -DCMAKE_BUILD_TYPE=Debug -B build cmake --build build --config Debug --target bpftime_runtime_tests -j$(nproc) - name: Test Runtime run: ./build/runtime/unit-test/bpftime_runtime_tests + - name: Generate runtime coverage (Ubuntu) + if: "matrix.container == 'ubuntu-2204'" + run: | + lcov --capture --directory . --output-file coverage-runtime.info --gcov-tool $(which gcov-12) + lcov --remove coverage-runtime.info '/usr/*' --output-file coverage-runtime.info + lcov --list coverage-runtime.info + - name: Generate runtime coverage (Fedora) + if: "matrix.container == 'fedora-39'" + run: | + lcov --capture --directory . --output-file coverage-runtime.info + lcov --remove coverage-runtime.info '/usr/*' --output-file coverage-runtime.info + lcov --list coverage-runtime.info + - name: Upload runtime coverage + uses: actions/upload-artifact@v4 + with: + name: coverage-runtime-${{matrix.container}} + include-hidden-files: false + path: | + ./coverage-runtime.info + - name: build runtime with mpk enable run: | rm -rf build - cmake -Bbuild -DBPFTIME_LLVM_JIT=YES -DBPFTIME_ENABLE_UNIT_TESTING=YES -DBPFTIME_ENABLE_MPK=YES -DCMAKE_BUILD_TYPE=Debug + cmake -Bbuild -DTEST_LCOV=ON -DBPFTIME_LLVM_JIT=YES -DBPFTIME_ENABLE_UNIT_TESTING=YES -DBPFTIME_ENABLE_MPK=YES -DCMAKE_BUILD_TYPE=Debug cmake --build build --config Debug --target bpftime_runtime_tests -j$(nproc) - - - name: test runtime with mpk run: ./build/runtime/unit-test/bpftime_runtime_tests + - name: Generate runtime with mpk enable coverage (Ubuntu) + if: "matrix.container == 'ubuntu-2204'" + run: | + lcov --capture --directory . --output-file coverage-runtime-mpk.info --gcov-tool $(which gcov-12) + lcov --remove coverage-runtime-mpk.info '/usr/*' --output-file coverage-runtime-mpk.info + lcov --list coverage-runtime-mpk.info + - name: Generate runtime with mpk enable coverage (Fedora) + if: "matrix.container == 'fedora-39'" + run: | + lcov --capture --directory . --output-file coverage-runtime-mpk.info + lcov --remove coverage-runtime-mpk.info '/usr/*' --output-file coverage-runtime-mpk.info + lcov --list coverage-runtime-mpk.info + - name: Upload runtime-mpk coverage + uses: actions/upload-artifact@v4 + with: + name: coverage-runtime-mpk-${{matrix.container}} + include-hidden-files: false + path: | + ./coverage-runtime-mpk.info + + - uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true # optional (default = false) + files: ./coverage-runtime.info + flags: runtime tests + token: ${{ secrets.CODECOV_TOKEN }} # required + verbose: true # optional (default = false) + \ No newline at end of file diff --git a/.github/workflows/test-verifier.yml b/.github/workflows/test-verifier.yml index 66862ebf..b2d2d602 100644 --- a/.github/workflows/test-verifier.yml +++ b/.github/workflows/test-verifier.yml @@ -19,10 +19,19 @@ jobs: run: | sudo apt-get update -y sudo apt-get install binutils-dev libboost1.74-all-dev libelf-dev zlib1g-dev ninja-build libyaml-cpp-dev -y + - name: Install lcov + run: | + sudo apt install -y lcov libzstd-dev - name: Build test target run: | - cmake -DBPFTIME_ENABLE_UNIT_TESTING=YES -DBPFTIME_LLVM_JIT=NO -DENABLE_EBPF_VERIFIER=YES -DCMAKE_BUILD_TYPE:STRING=Release -S . -B build -G Ninja + cmake -DTEST_LCOV=ON -DBPFTIME_ENABLE_UNIT_TESTING=YES -DBPFTIME_LLVM_JIT=NO -DENABLE_EBPF_VERIFIER=YES -DCMAKE_BUILD_TYPE:STRING=Release -S . -B build -G Ninja cmake --build build --config Release --target bpftime_verifier_tests - name: Run tests run: | ./build/bpftime-verifier/bpftime_verifier_tests + - name: upload runtime coverage + run: | + lcov --capture --directory . --output-file coverage.info + lcov --remove coverage.info '/usr/*' --output-file coverage.info + lcov --list coverage.info + diff --git a/README.md b/README.md index 173a2a30..89759ae6 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ `bpftime` is a High-Performance userspace eBPF runtime and General Extension Framework designed for userspace. It enables faster Uprobe, USDT, Syscall hooks, XDP, and more event sources by bypassing the kernel and utilizing an optimized compiler like `LLVM`. -> ⚠️ **Note**: `bpftime` is actively under development, and it's not yet recommended for production use now. See our [roadmap](#roadmap) for details. We'd love to hear your feedback and suggestions! Please feel free to open an issue or [Contact us](#contact-and-citations). - 📦 [Key Features](#key-features) \ 🔨 [Quick Start](#quick-start) \ 🔌 [Examples & Use Cases](#examples--use-cases) \ @@ -19,6 +17,8 @@ bpftime is not `userspace eBPF VM`, it's a userspace runtime framework includes everything to run eBPF in userspace: `loader`, `verifier`, `helpers`, `maps`, `ufunc` and multiple `events` such as Observability, Network, Policy or Access Control. It has multiple VM backend options support. For eBPF VM only, please see [llvmbpf](https://github.com/eunomia-bpf/llvmbpf). +> ⚠️ **Note**: `bpftime` is currently under active development and refactoring towards v2. It may contain bugs or unstable API. Please use it with caution. For more details, check our [roadmap](#roadmap). We'd love to hear your feedback and suggestions! Feel free to open an issue or [Contact us](#contact-and-citations). + ## Why bpftime? What's the design Goal? - **Performance Gains**: Achieve better performance by `bypassing the kernel` (e.g., via `Userspace DBI` or `Network Drivers`), with more configurable, optimized and more arch supported JIT/AOT options like `LLVM`, while maintaining compatibility with Linux kernel eBPF. @@ -120,8 +120,8 @@ Examples including: - [opensnoop](https://github.com/eunomia-bpf/bpftime/tree/master/example/opensnoop) for trace file open syscalls. - More [bcc/libbpf-tools](https://github.com/eunomia-bpf/bpftime/tree/master/example/libbpf-tools). - Run with [bpftrace](https://github.com/eunomia-bpf/bpftime/tree/master/example/bpftrace) commands or scripts. -- [error injection](https://github.com/eunomia-bpf/bpftime/tree/master/example/error-injection): change function behavior with `bpf_override_return`. -- Use the eBPF LLVM JIT/AOT vm as [a standalone library](https://github.com/eunomia-bpf/bpftime/tree/master/vm/llvm-jit/example). +- [error injection](https://github.com/eunomia-bpf/bpftime/tree/master/example/error-inject): change function behavior with `bpf_override_return`. +- Use the eBPF LLVM JIT/AOT vm as [a standalone library](https://github.com/eunomia-bpf/llvmbpf/tree/main/example). - Userspace [XDP with DPDK and AF_XDP](https://github.com/userspace-xdp/userspace-xdp) ## In-Depth @@ -197,6 +197,7 @@ See [eunomia.dev/bpftime/documents/build-and-test](https://eunomia.dev/bpftime/d `bpftime` is continuously evolving with more features in the pipeline: - [ ] Keep compatibility with the evolving kernel +- [ ] Refactor for General Extension Framework - [ ] Trying to refactor, bug fixing for `Production`. - [ ] More examples and usecases: - [X] Userspace Network Driver on userspace eBPF diff --git a/attach/frida_uprobe_attach_impl/CMakeLists.txt b/attach/frida_uprobe_attach_impl/CMakeLists.txt index 258c0893..ccb44b4c 100644 --- a/attach/frida_uprobe_attach_impl/CMakeLists.txt +++ b/attach/frida_uprobe_attach_impl/CMakeLists.txt @@ -23,8 +23,13 @@ set(TEST_SOURCES test/test_replace_attach_with_override.cpp test/test_attach_with_unified_interface.cpp ) +option(TEST_LCOV "option for lcov" OFF) add_executable(bpftime_frida_uprobe_attach_tests ${TEST_SOURCES}) +if (${TEST_LCOV}) + target_compile_options(bpftime_frida_uprobe_attach_tests PRIVATE -fprofile-arcs -ftest-coverage) +endif() + if(${ENABLE_EBPF_VERIFIER} AND NOT TARGET Catch2) message(STATUS "Adding Catch2 by FetchContent for frida_uprobe_attach_impl") Include(FetchContent) @@ -39,7 +44,12 @@ if(${ENABLE_EBPF_VERIFIER} AND NOT TARGET Catch2) endif() add_dependencies(bpftime_frida_uprobe_attach_tests Catch2 bpftime_frida_uprobe_attach_impl spdlog::spdlog) -target_link_libraries(bpftime_frida_uprobe_attach_tests PRIVATE Catch2::Catch2WithMain bpftime_frida_uprobe_attach_impl spdlog::spdlog) +if (${TEST_LCOV}) + target_link_options(bpftime_frida_uprobe_attach_tests PRIVATE -lgcov) + target_link_libraries(bpftime_frida_uprobe_attach_tests PRIVATE Catch2::Catch2WithMain bpftime_frida_uprobe_attach_impl spdlog::spdlog gcov) +else () + target_link_libraries(bpftime_frida_uprobe_attach_tests PRIVATE Catch2::Catch2WithMain bpftime_frida_uprobe_attach_impl spdlog::spdlog) +endif() target_include_directories(bpftime_frida_uprobe_attach_tests PRIVATE ${FRIDA_UPROBE_ATTACH_IMPL_INCLUDE} ${Catch2_INCLUDE} ${SPDLOG_INCLUDE}) add_test(NAME bpftime_frida_uprobe_attach_tests COMMAND bpftime_frida_uprobe_attach_tests) diff --git a/attach/syscall_trace_attach_impl/CMakeLists.txt b/attach/syscall_trace_attach_impl/CMakeLists.txt index e94da71e..2241cab2 100644 --- a/attach/syscall_trace_attach_impl/CMakeLists.txt +++ b/attach/syscall_trace_attach_impl/CMakeLists.txt @@ -28,8 +28,13 @@ set(TEST_SOURCES test/test_private_data_parsing.cpp test/test_syscall_dispatch.cpp ) +option(TEST_LCOV "option for lcov" OFF) add_executable(bpftime_syscall_trace_attach_tests ${TEST_SOURCES}) +if (${TEST_LCOV}) + target_compile_options(bpftime_syscall_trace_attach_tests PRIVATE -fprofile-arcs -ftest-coverage) +endif() + if(${ENABLE_EBPF_VERIFIER} AND NOT TARGET Catch2) message(STATUS "Adding Catch2 by FetchContent at syscall_trace_attach_impl") Include(FetchContent) @@ -44,7 +49,12 @@ if(${ENABLE_EBPF_VERIFIER} AND NOT TARGET Catch2) endif() add_dependencies(bpftime_syscall_trace_attach_tests Catch2 bpftime_syscall_trace_attach_impl spdlog::spdlog) -target_link_libraries(bpftime_syscall_trace_attach_tests PRIVATE Catch2::Catch2WithMain bpftime_syscall_trace_attach_impl spdlog::spdlog) +if (${TEST_LCOV}) + target_link_options(bpftime_syscall_trace_attach_tests PRIVATE -lgcov) + target_link_libraries(bpftime_syscall_trace_attach_tests PRIVATE Catch2::Catch2WithMain bpftime_syscall_trace_attach_impl spdlog::spdlog gcov) +else () + target_link_libraries(bpftime_syscall_trace_attach_tests PRIVATE Catch2::Catch2WithMain bpftime_syscall_trace_attach_impl spdlog::spdlog) +endif() target_include_directories(bpftime_syscall_trace_attach_tests PRIVATE ${SYSCALL_TRACE_ATTACH_IMPL_INCLUDE} ${Catch2_INCLUDE} ${SPDLOG_INCLUDE}) add_test(NAME bpftime_syscall_trace_attach_tests COMMAND bpftime_syscall_trace_attach_tests) diff --git a/benchmark/uprobe/uprobe.bpf.c b/benchmark/uprobe/uprobe.bpf.c index a91e0ef9..6b7fa23c 100644 --- a/benchmark/uprobe/uprobe.bpf.c +++ b/benchmark/uprobe/uprobe.bpf.c @@ -58,7 +58,9 @@ SEC("uprobe/benchmark/test:__bench_write") int BPF_UPROBE(__bench_write, char *a, int b, uint64_t c) { char buffer[5] = "text"; - bpf_probe_write_user(a, buffer, sizeof(buffer)); + for (int i = 0; i < 1000; i++) { + bpf_probe_write_user(a, buffer, sizeof(buffer)); + } return b + c; } @@ -66,7 +68,10 @@ SEC("uprobe/benchmark/test:__bench_read") int BPF_UPROBE(__bench_read, char *a, int b, uint64_t c) { char buffer[5]; - int res = bpf_probe_read_user(buffer, sizeof(buffer), a); + int res; + for (int i = 0; i < 1000; i++) { + bpf_probe_read_user(buffer, sizeof(buffer), a); + } return b + c + res + buffer[1]; } diff --git a/bpftime-verifier/CMakeLists.txt b/bpftime-verifier/CMakeLists.txt index 2b93d5d4..5ab5be9e 100644 --- a/bpftime-verifier/CMakeLists.txt +++ b/bpftime-verifier/CMakeLists.txt @@ -34,8 +34,20 @@ if(NOT TARGET Catch2) FetchContent_MakeAvailable(Catch2) endif() +option(TEST_LCOV "option for lcov" OFF) add_executable(bpftime_verifier_tests ${TEST_SOURCES}) + +if (${TEST_LCOV}) + target_compile_options(bpftime_verifier_tests PRIVATE -fprofile-arcs -ftest-coverage) +endif() + add_dependencies(bpftime_verifier_tests bpftime-verifier) -target_link_libraries(bpftime_verifier_tests PRIVATE bpftime-verifier Catch2::Catch2WithMain) + +if (${TEST_LCOV}) + target_link_options(bpftime_verifier_tests PRIVATE -lgcov) + target_link_libraries(bpftime_verifier_tests PRIVATE bpftime-verifier Catch2::Catch2WithMain gcov) +else () + target_link_libraries(bpftime_verifier_tests PRIVATE bpftime-verifier Catch2::Catch2WithMain) +endif() target_include_directories(bpftime_verifier_tests PRIVATE ${BPFTIME_VERIFIER_INCLUDE}) add_test(NAME bpftime_verifier_tests COMMAND bpftime_verifier_tests) diff --git a/runtime/unit-test/CMakeLists.txt b/runtime/unit-test/CMakeLists.txt index d48ce7c6..9a1332a4 100644 --- a/runtime/unit-test/CMakeLists.txt +++ b/runtime/unit-test/CMakeLists.txt @@ -32,10 +32,23 @@ set(TEST_SOURCES tailcall/test_user_to_kernel_tailcall.cpp ) +option(TEST_LCOV "option for lcov" OFF) add_executable(bpftime_runtime_tests ${TEST_SOURCES}) + +if (${TEST_LCOV}) + target_compile_options(bpftime_runtime_tests PRIVATE -fprofile-arcs -ftest-coverage) +endif() + set_property(TARGET bpftime_runtime_tests PROPERTY CXX_STANDARD 20) add_dependencies(bpftime_runtime_tests runtime bpftime-object bpftime_frida_uprobe_attach_impl) -target_link_libraries(bpftime_runtime_tests PRIVATE runtime bpftime-object Catch2::Catch2WithMain bpftime_frida_uprobe_attach_impl) + +if (${TEST_LCOV}) + target_link_options(bpftime_runtime_tests PRIVATE -lgcov) + target_link_libraries(bpftime_runtime_tests PRIVATE runtime bpftime-object Catch2::Catch2WithMain bpftime_frida_uprobe_attach_impl gcov) +else () + target_link_libraries(bpftime_runtime_tests PRIVATE runtime bpftime-object Catch2::Catch2WithMain bpftime_frida_uprobe_attach_impl) +endif() + target_include_directories(bpftime_runtime_tests PRIVATE ${BPFTIME_RUNTIME_INCLUDE} ${BPFTIME_OBJECT_INCLUDE_DIRS} ${Catch2_INCLUDE} ${Boost_INCLUDE} ${FRIDA_UPROBE_ATTACH_IMPL_INCLUDE}) add_test(NAME bpftime_runtime_tests COMMAND bpftime_runtime_tests)