diff --git a/.github/actions/generate-build-matrix/generate_matrix.py b/.github/actions/generate-build-matrix/generate_matrix.py index a7d7e9f7d..4b8db62af 100644 --- a/.github/actions/generate-build-matrix/generate_matrix.py +++ b/.github/actions/generate-build-matrix/generate_matrix.py @@ -7,12 +7,16 @@ def get_default_combinations(event_name, all_combinations): """Gets the default build combinations based on the GitHub event type.""" - if event_name in ("push", "pull_request", "pull_request_target"): + if event_name in ( + "push", + "pull_request", + "pull_request_target", + "issue_comment", + "workflow_dispatch", + ): return ["gcc/none"] - elif event_name == "issue_comment": - return ["gcc/none", "clang/none"] - elif event_name == "workflow_dispatch": - return all_combinations + elif event_name == "schedule": + return ["gcc/perfetto"] else: # Default to a minimal safe configuration for unknown events return ["gcc/none"] @@ -25,10 +29,12 @@ def main(): "gcc/asan", "gcc/tsan", "gcc/valgrind", + "gcc/perfetto", "clang/none", "clang/asan", "clang/tsan", "clang/valgrind", + "clang/perfetto", ] user_input = os.getenv("USER_INPUT", "") comment_body = os.getenv("COMMENT_BODY", "") diff --git a/.github/workflows/cmake-build.yaml b/.github/workflows/cmake-build.yaml index 310f79a41..25cc989cf 100644 --- a/.github/workflows/cmake-build.yaml +++ b/.github/workflows/cmake-build.yaml @@ -7,6 +7,8 @@ on: types: [created] push: branches: [ main, develop ] + schedule: + - cron: '0 18 * * 6' workflow_dispatch: inputs: ref: @@ -21,9 +23,19 @@ on: - `all` (run all combinations) - `all -clang/none -clang/valgrind` (run all except specified) - `+clang/none +clang/valgrind` (run default matrix plus specified) - Default (if empty): Run all except clang/none and clang/valgrind. + Default (if empty): Run `gcc/none` required: false default: '' + perfetto-heap-profile: + description: "Enable heap profiling for Perfetto runs" + required: false + type: boolean + default: false + perfetto-cpu-profile: + description: "Enable CPU profiling for Perfetto runs" + required: false + type: boolean + default: true workflow_call: inputs: checkout-path: @@ -76,6 +88,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || github.event_name == 'push' || + github.event_name == 'schedule' || github.event_name == 'workflow_call' || ( github.event_name == 'issue_comment' && @@ -190,6 +203,7 @@ jobs: container: image: ghcr.io/framework-r-d/phlex-ci:latest + options: --cap-add=SYS_PTRACE --security-opt seccomp=unconfined steps: - name: Check out code @@ -224,6 +238,7 @@ jobs: extra-options: | ${{ matrix.sanitizer == 'asan' && format('-D{0}_ENABLE_ASAN=ON', steps.repo_name.outputs.name) || '' }} ${{ matrix.sanitizer == 'tsan' && format('-D{0}_ENABLE_TSAN=ON', steps.repo_name.outputs.name) || '' }} + ${{ matrix.sanitizer == 'perfetto' && format('-D{0}_ENABLE_PERFETTO=ON', steps.repo_name.outputs.name) || '' }} - name: Build id: build @@ -232,7 +247,7 @@ jobs: build-path: ${{ env.local_build_path }} - name: Run tests - if: matrix.sanitizer != 'valgrind' + if: matrix.sanitizer != 'valgrind' && matrix.sanitizer != 'perfetto' run: | . /entrypoint.sh cd "$GITHUB_WORKSPACE/${{ env.local_build_path }}" @@ -264,6 +279,62 @@ jobs: echo "⚠️ Valgrind tests failed, but the workflow will continue." fi + - name: Run Perfetto profiling + if: matrix.sanitizer == 'perfetto' + env: + PERFETTO_HEAP_PROFILE: ${{ github.event.inputs.perfetto-heap-profile || 'false' }} + PERFETTO_CPU_PROFILE: ${{ github.event.inputs.perfetto-cpu-profile || 'true' }} + run: | + . /entrypoint.sh + cd "$GITHUB_WORKSPACE/${{ env.local_build_path }}" + + echo "➡️ Running tests with Perfetto profiling..." + + # Set perf_event_paranoid for CPU profiling + if [ "$PERFETTO_CPU_PROFILE" = "true" ]; then + echo "Configuring perf_event_paranoid for CPU profiling" + echo -1 | tee /proc/sys/kernel/perf_event_paranoid 2>/dev/null || echo "Warning: Could not set perf_event_paranoid" + fi + + # Configure profiling based on environment + TRACEBOX_ARGS="" + if [ "$PERFETTO_HEAP_PROFILE" = "true" ]; then + echo "Enabling heap profiling" + TRACEBOX_ARGS="$TRACEBOX_ARGS --app '*' --heapprofd" + fi + if [ "$PERFETTO_CPU_PROFILE" = "true" ]; then + echo "Enabling CPU profiling" + TRACEBOX_ARGS="$TRACEBOX_ARGS --cpu-freq --cpu-idle --cpu-sched" + fi + + # Run tests with or without tracebox wrapper + if [ -n "$TRACEBOX_ARGS" ]; then + echo "::group::Running ctest with tracebox" + tracebox $TRACEBOX_ARGS -o "$GITHUB_WORKSPACE/${{ env.local_build_path }}/perfetto-trace.pftrace" -- ctest --progress --output-on-failure -j "$(nproc)" || TEST_RESULT=$? + else + echo "::group::Running ctest with Perfetto SDK tracing" + ctest --progress --output-on-failure -j "$(nproc)" || TEST_RESULT=$? + fi + + echo "::endgroup::" + if [ "${TEST_RESULT:-0}" -eq 0 ]; then + echo "✅ Perfetto profiling completed." + else + echo "::error:: Perfetto profiling failed." + exit 1 + fi + + - name: Upload Perfetto traces + if: matrix.sanitizer == 'perfetto' + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: perfetto-traces-${{ matrix.compiler }} + path: | + ${{ env.local_build_path }}/**/*.pftrace + ${{ env.local_build_path }}/**/perfetto-trace.pftrace + retention-days: 30 + if-no-files-found: warn + cmake-build-skipped: needs: [pre-check, detect-changes] if: > diff --git a/.gitignore b/.gitignore index 931fc4922..23bce7796 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /_deps/ /build-*/ /build/ +/out/ /phlex-build/ /phlex-src/ CMakeFiles/ diff --git a/CMakeLists.txt b/CMakeLists.txt index a5547316f..008824b6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ include(Modules/private/CreateCoverageTargets.cmake) option(ENABLE_TSAN "Enable Thread Sanitizer" OFF) option(ENABLE_ASAN "Enable Address Sanitizer" OFF) +option(ENABLE_PERFETTO "Enable Perfetto profiling" OFF) option(PHLEX_USE_FORM "Enable experimental integration with FORM" OFF) option(ENABLE_COVERAGE "Enable code coverage instrumentation" OFF) option(ENABLE_CLANG_TIDY "Enable clang-tidy checks during build" OFF) @@ -145,6 +146,12 @@ if(ENABLE_ASAN) endif() endif() +# Configure Perfetto profiling if enabled +if(ENABLE_PERFETTO) + message(STATUS "Enabling Perfetto profiling") + find_package(Perfetto REQUIRED) +endif() + # Configure code coverage if enabled if(ENABLE_COVERAGE) # Check if the compiler supports code coverage diff --git a/Modules/FindPerfetto.cmake b/Modules/FindPerfetto.cmake new file mode 100644 index 000000000..093f7a2b1 --- /dev/null +++ b/Modules/FindPerfetto.cmake @@ -0,0 +1,31 @@ +# FindPerfetto.cmake +# Finds the Perfetto SDK (single-header implementation) + +include(FindPackageHandleStandardArgs) + +find_path( + Perfetto_INCLUDE_DIR + NAMES perfetto.h + PATHS /opt/perfetto /usr/local/include /usr/include + DOC "Perfetto SDK header location" +) + +find_file( + Perfetto_SOURCE + NAMES perfetto.cc + PATHS /opt/perfetto /usr/local/include /usr/include + DOC "Perfetto SDK implementation file" +) + +find_package_handle_standard_args(Perfetto REQUIRED_VARS Perfetto_INCLUDE_DIR Perfetto_SOURCE) + +if(Perfetto_FOUND AND NOT TARGET Perfetto::Perfetto) + find_package(Threads REQUIRED) + add_library(perfetto_impl STATIC "${Perfetto_SOURCE}") + target_include_directories(perfetto_impl PUBLIC "${Perfetto_INCLUDE_DIR}") + target_compile_definitions(perfetto_impl PUBLIC PERFETTO_ENABLE_TRACING=1) + target_link_libraries(perfetto_impl PUBLIC Threads::Threads) + add_library(Perfetto::Perfetto ALIAS perfetto_impl) +endif() + +mark_as_advanced(Perfetto_INCLUDE_DIR Perfetto_SOURCE) diff --git a/ci/Dockerfile b/ci/Dockerfile index 3b6087e7c..ba201b5d9 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -280,6 +280,32 @@ set -euo pipefail rm -f /tmp/spack.yaml CLEAN_TEMP_FILES +######################################################################## +# Install Perfetto SDK for profiling + +RUN <<'INSTALL_PERFETTO' +set -euo pipefail + +# Install Perfetto SDK and tools +apt-get update +apt-get install -y --no-install-recommends \ + wget +apt-get clean +rm -rf /var/lib/apt/lists/* + +mkdir -p /opt/perfetto +cd /opt/perfetto +PERFETTO_VERSION=v51.0 +wget -O perfetto.h https://raw.githubusercontent.com/google/perfetto/${PERFETTO_VERSION}/sdk/perfetto.h +wget -O perfetto.cc https://raw.githubusercontent.com/google/perfetto/${PERFETTO_VERSION}/sdk/perfetto.cc +chmod 644 perfetto.h perfetto.cc + +# Install tracebox for system-wide profiling +wget -O tracebox https://get.perfetto.dev/tracebox +chmod +x tracebox +mv tracebox /usr/local/bin/ +INSTALL_PERFETTO + ######################################################################## # Finalize CI image stage diff --git a/phlex/app/CMakeLists.txt b/phlex/app/CMakeLists.txt index 3c0a57af0..3f1fd76c1 100644 --- a/phlex/app/CMakeLists.txt +++ b/phlex/app/CMakeLists.txt @@ -33,4 +33,8 @@ cet_make_exec( jsonnet::lib ) +if(ENABLE_PERFETTO) + target_link_libraries(phlex PRIVATE Perfetto::Perfetto) +endif() + set_target_properties(phlex PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")