diff --git a/.github/workflows/test-examples.yml b/.github/workflows/test-examples.yml index e4c851be..3a275740 100644 --- a/.github/workflows/test-examples.yml +++ b/.github/workflows/test-examples.yml @@ -28,11 +28,20 @@ jobs: - uses: actions/checkout@v2 with: submodules: 'recursive' + - name: Install lcov (Ubuntu) + if: ${{matrix.container.name=='ubuntu'}} + run: | + apt-get install -y lcov tree + - name: Install lcov (Fedora) + if: ${{matrix.container.name =='fedora'}} + run: | + dnf install -y lcov tree - name: Build and install runtime (with llvm-jit) if: ${{matrix.enable_jit}} run: | cmake -Bbuild -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo \ -DBPFTIME_LLVM_JIT=1 \ + -DTEST_LCOV=YES \ -DBUILD_BPFTIME_DAEMON=1 \ -DCMAKE_CXX_FLAGS="-DDEFAULT_LOGGER_OUTPUT_PATH='\"console\"'" cmake --build build --config RelWithDebInfo --target install -j @@ -41,12 +50,26 @@ jobs: run: | cmake -Bbuild -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo \ -DBPFTIME_LLVM_JIT=0 \ + -DTEST_LCOV=YES \ -DBUILD_BPFTIME_DAEMON=1 \ -DCMAKE_CXX_FLAGS="-DDEFAULT_LOGGER_OUTPUT_PATH='\"console\"'" cmake --build build --config RelWithDebInfo --target install -j - name: Build basic examples run: | make -C example -j + - name: Package gcno files + run: | + find . -type f -name "*.gcno" | tar --create --gzip --file=gcno_files.tar.gz --files-from=- + ls -la + pwd + tree . + - name: Upload gcno file + uses: actions/upload-artifact@v4 + with: + name: gcno-files-${{matrix.enable_jit && 'jit' || 'no-jit'}}-${{matrix.container.name}} + include-hidden-files: true + path: | + ./gcno_files.tar.gz - name: Upload build results (without jit) uses: actions/upload-artifact@v4 if: ${{!matrix.enable_jit}} @@ -104,68 +127,84 @@ jobs: victim: ./victim syscall_trace: true expected_str: " 0 test.txt" + name: opensnoop-libbpf-tools - path: libbpf-tools/statsnoop executable: ./statsnoop victim: ./victim syscall_trace: true expected_str: "victim 0 0 /sys" + name: statsnoop - path: malloc executable: ./malloc victim: ./victim syscall_trace: false expected_str: "malloc calls: " + name: malloc - path: minimal executable: ./uprobe victim: ./victim syscall_trace: false expected_str: "" + name: minimal - path: opensnoop executable: ./opensnoop victim: ./victim syscall_trace: true expected_str: " 0 test.txt" + name: opensnoop - path: sslsniff executable: ./sslsniff victim: /bin/wget https://www.google.com syscall_trace: false expected_str: "----- DATA -----" + name: sslsniff - path: libbpf-tools/bashreadline executable: ./readline victim: /bin/bash syscall_trace: false expected_str: "info" + name: bashreadline - path: libbpf-tools/syscount executable: ./syscount victim: /bin/bash syscall_trace: false expected_str: "info" + name: syscount - path: libbpf-tools/funclatency executable: ./funclatency -i 1 ./victim:plus victim: ./victim syscall_trace: false expected_str: "|*" + name: funclatency - path: libbpf-tools/mountsnoop executable: ./mountsnoop victim: ./victim syscall_trace: true expected_str: mount( + name: mountsnoop - path: libbpf-tools/sigsnoop executable: ./sigsnoop victim: ./victim syscall_trace: true expected_str: "victim 0 -1 0" + name: sigsnoop - path: tailcall_minimal executable: ./tailcall_minimal victim: ./victim syscall_trace: false expected_str: "See /sys/kernel/debug/tracing/trace_pipe for output (15)" + name: tailcall_minimal - path: usdt_minimal executable: ./usdt_minimal victim: ./victim syscall_trace: false expected_str: "bpf:" + name: usdt_minimal steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' - name: Download prebuilt runtime (with jit) if: ${{matrix.enable_jit}} uses: actions/download-artifact@v4 @@ -178,6 +217,16 @@ jobs: with: name: runtime-package-no-jit-${{matrix.container.name}} path: ~/.bpftime + - name: Download gcno files + uses: actions/download-artifact@v4 + with: + name: gcno-files-${{matrix.enable_jit && 'jit' || 'no-jit'}}-${{matrix.container.name}} + path: . + - name: Extract gcno files + run: | + ls -lah . + tar -zxvf gcno_files.tar.gz + pwd - name: Install which(required by funclatency on fedora) if: ${{matrix.container.name=='fedora' && matrix.examples.path=='libbpf-tools/funclatency'}} run: | @@ -188,9 +237,6 @@ jobs: - name: Show downloaded artifacts run: | ls ~/.bpftime - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - name: Build test assets run: | make -C example/${{matrix.examples.path}} -j @@ -212,9 +258,38 @@ jobs: ROOT=$(pwd) cd example/${{matrix.examples.path}} python3 $ROOT/.github/script/run_example.py "${{matrix.examples.executable}}" "${{matrix.examples.victim}}" "${{matrix.examples.expected_str}}" "/github/home/.bpftime/bpftime -i /github/home/.bpftime" 0 + - name: Generate example coverage (Ubuntu) + if: "matrix.container.image == 'ubuntu-2204'" + run: | + apt-get install -y gpg lcov + lcov --capture --directory . --directory .github --directory /github/home/.bpftime --output-file coverage-example.info --gcov-tool $(which gcov-12) + lcov --remove coverage-example.info '/usr/*' --output-file coverage-example.info + lcov --list coverage-example.info + - name: Generate example coverage (Fedora) + if: "matrix.container.image == 'fedora-39'" + run: | + dnf install -y lcov + lcov --capture --directory . --output-file coverage-example.info + lcov --remove coverage-example.info '/usr/*' --output-file coverage-example.info + lcov --list coverage-example.info + - name: Upload example coverage + uses: actions/upload-artifact@v4 + with: + name: coverage-example-${{matrix.container.name}}-${{matrix.enable_jit}}-${{matrix.examples.name}}-${{matrix.privilege_options.enable}} + include-hidden-files: false + path: | + ./coverage-example.info + - uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true # optional (default = false) + files: ./coverage-example.info + flags: coverage-example-${{matrix.container.name}}-${{matrix.enable_jit}}-${{matrix.examples.name}}-${{matrix.privilege_options.enable}} + token: ${{ secrets.CODECOV_TOKEN }} # required + verbose: true # optional (default = false) + - name: Setup tmate session # Setup SSH when manually triggered and failing, so we can debug CI more conveniently if: "${{ failure() && github.event_name == 'workflow_dispatch' }}" uses: mxschmitt/action-tmate@v3 with: - limit-access-to-actor: true + limit-access-to-actor: false diff --git a/Dockerfile b/Dockerfile index bec0d054..230b2e3b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,4 +8,4 @@ RUN git submodule update --init --recursive ENV CXX=g++ ENV CC=gcc RUN make release -ENV PATH="${PATH}:/root/.bpftime/" \ No newline at end of file +ENV PATH="${PATH}:/root/.bpftime/" diff --git a/attach/base_attach_impl/base_attach_impl.hpp b/attach/base_attach_impl/base_attach_impl.hpp index ba55af57..2c78f359 100644 --- a/attach/base_attach_impl/base_attach_impl.hpp +++ b/attach/base_attach_impl/base_attach_impl.hpp @@ -5,6 +5,7 @@ #include #include #include "attach_private_data.hpp" +#include namespace bpftime { namespace attach @@ -71,7 +72,8 @@ inline uint64_t bpftime_set_retval(uint64_t value) } else { spdlog::error( "Called bpftime_set_retval, but no retval callback was set"); - assert(false); + throw std::invalid_argument( + "Called bpftime_set_retval, but no retval callback was set"); } return 0; } @@ -87,7 +89,8 @@ inline uint64_t bpftime_override_return(uint64_t ctx, uint64_t value) } else { spdlog::error( "Called bpftime_override_return, but no retval callback was set"); - assert(false); + throw std::invalid_argument( + "Called bpftime_override_return, but no retval callback was set"); } return 0; } diff --git a/attach/frida_uprobe_attach_impl/CMakeLists.txt b/attach/frida_uprobe_attach_impl/CMakeLists.txt index f211cce8..ad2574dc 100644 --- a/attach/frida_uprobe_attach_impl/CMakeLists.txt +++ b/attach/frida_uprobe_attach_impl/CMakeLists.txt @@ -22,6 +22,8 @@ set(TEST_SOURCES test/test_filter_attach_with_override.cpp test/test_replace_attach_with_override.cpp test/test_attach_with_unified_interface.cpp + test/test_attach_private_data_parsing.cpp + test/test_base_attach_impl.cpp ) option(TEST_LCOV "option for lcov" OFF) add_executable(bpftime_frida_uprobe_attach_tests ${TEST_SOURCES}) diff --git a/attach/frida_uprobe_attach_impl/test/test_attach_private_data_parsing.cpp b/attach/frida_uprobe_attach_impl/test/test_attach_private_data_parsing.cpp new file mode 100644 index 00000000..f53f5ccc --- /dev/null +++ b/attach/frida_uprobe_attach_impl/test/test_attach_private_data_parsing.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +using namespace bpftime; + +TEST_CASE("Test illegal parsing") +{ + SECTION("Test bad strings") + { + auto priv = std::make_unique< + bpftime::attach::frida_attach_private_data>(); + REQUIRE(priv->initialize_from_string("aaa:") == -EINVAL); + } +} diff --git a/attach/frida_uprobe_attach_impl/test/test_base_attach_impl.cpp b/attach/frida_uprobe_attach_impl/test/test_base_attach_impl.cpp new file mode 100644 index 00000000..7a98ce36 --- /dev/null +++ b/attach/frida_uprobe_attach_impl/test/test_base_attach_impl.cpp @@ -0,0 +1,32 @@ +#include "catch2/catch_test_macros.hpp" +#include +#include +#include + +using namespace bpftime::attach; +using namespace bpftime; + +TEST_CASE("Test bpftime_set_retval") +{ + curr_thread_override_return_callback.reset(); + REQUIRE_THROWS(bpftime_set_retval(0)); + bool called = false; + curr_thread_override_return_callback = [&](uint64_t, uint64_t) { + called = true; + }; + bpftime_set_retval(0); + REQUIRE(called); +} + +TEST_CASE("Test bpftime_override_return") +{ + curr_thread_override_return_callback.reset(); + REQUIRE_THROWS(bpftime_override_return(0, 0)); + + bool called = false; + curr_thread_override_return_callback = [&](uint64_t, uint64_t) { + called = true; + }; + bpftime_override_return(0, 0); + REQUIRE(called); +} diff --git a/attach/text_segment_transformer/CMakeLists.txt b/attach/text_segment_transformer/CMakeLists.txt index d93dd031..d06da96d 100644 --- a/attach/text_segment_transformer/CMakeLists.txt +++ b/attach/text_segment_transformer/CMakeLists.txt @@ -15,3 +15,8 @@ target_include_directories(bpftime_text_segment_transformer ${FRIDA_GUM_INSTALL_DIR} ${SPDLOG_INCLUDE} ) + +if (${TEST_LCOV}) + target_compile_options(bpftime_text_segment_transformer PRIVATE -fprofile-arcs -ftest-coverage -fprofile-update=atomic) + target_link_options(bpftime_text_segment_transformer PRIVATE -fprofile-arcs) +endif() diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index a6e89870..de7f4644 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -12,9 +12,9 @@ set(PLACEHOLDER_INPUT ${CMAKE_CURRENT_SOURCE_DIR}/assets/placeholder) set(PLACEHOLDER_OUTPUT placeholder.c) add_custom_command( - OUTPUT placeholder.c - COMMAND embedfile placeholder ${PLACEHOLDER_INPUT} ${PLACEHOLDER_OUTPUT} - DEPENDS ${PLACEHOLDER_INPUT}) + OUTPUT placeholder.c + COMMAND embedfile placeholder ${PLACEHOLDER_INPUT} ${PLACEHOLDER_OUTPUT} + DEPENDS ${PLACEHOLDER_INPUT}) add_library(libbpftime_daemon STATIC user/bpf_tracer.cpp @@ -63,3 +63,8 @@ install(TARGETS bpftime_daemon CONFIGURATIONS Release Debug RelWithDebInfo DESTI if(BPFTIME_ENABLE_UNIT_TESTING) add_subdirectory(test) endif() + +if(${TEST_LCOV}) + target_compile_options(bpftime_daemon PUBLIC -fprofile-arcs -ftest-coverage -fprofile-update=atomic) + target_link_options(bpftime_daemon PUBLIC -fprofile-arcs) +endif() diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index fca3324f..6d6f7716 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -245,3 +245,8 @@ if(BPFTIME_ENABLE_UNIT_TESTING AND BPFTIME_BUILD_WITH_LIBBPF) add_subdirectory(test) add_subdirectory(unit-test) endif() + +if (${TEST_LCOV}) + target_compile_options(${PROJECT_NAME} PUBLIC -fprofile-arcs -ftest-coverage -fprofile-update=atomic) + target_link_options(${PROJECT_NAME} PUBLIC -fprofile-arcs) +endif() diff --git a/runtime/agent/CMakeLists.txt b/runtime/agent/CMakeLists.txt index 49f43842..925e3fcb 100644 --- a/runtime/agent/CMakeLists.txt +++ b/runtime/agent/CMakeLists.txt @@ -10,6 +10,12 @@ set_target_properties(bpftime-agent PROPERTIES CXX_STANDARD 20 LINK_DEPENDS ${CM if(UNIX AND NOT APPLE) target_link_options(bpftime-agent PRIVATE -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/agent.version) endif() + +if (${TEST_LCOV}) + target_compile_options(bpftime-agent PRIVATE -fprofile-arcs -ftest-coverage -fprofile-update=atomic) + target_link_options(bpftime-agent PRIVATE -fprofile-arcs) +endif() + if(${BPFTIME_BUILD_WITH_LIBBPF}) target_include_directories(bpftime-agent PRIVATE diff --git a/runtime/syscall-server/CMakeLists.txt b/runtime/syscall-server/CMakeLists.txt index 6df0b224..b49c0340 100644 --- a/runtime/syscall-server/CMakeLists.txt +++ b/runtime/syscall-server/CMakeLists.txt @@ -49,3 +49,8 @@ if(${ENABLE_EBPF_VERIFIER}) target_include_directories(bpftime-syscall-server PRIVATE ${BPFTIME_VERIFIER_INCLUDE}) target_compile_definitions(bpftime-syscall-server PRIVATE ENABLE_EBPF_VERIFIER ENABLE_BPFTIME_VERIFIER) endif() + +if (${TEST_LCOV}) + target_compile_options(bpftime-syscall-server PRIVATE -fprofile-arcs -ftest-coverage -fprofile-update=atomic) + target_link_options(bpftime-syscall-server PRIVATE -fprofile-arcs) +endif()