From f2384a47f1b6d34bc6658a83eb8aa301e2771304 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Fri, 2 May 2025 12:49:51 +0100 Subject: [PATCH] feat: Implement clang build analyzer (#2072) Fix: https://github.com/XRPLF/clio/issues/936 --- .github/actions/generate/action.yml | 6 +++ .github/actions/prepare_runner/action.yml | 12 ++++- .github/workflows/build.yml | 58 +++++++++++++++++++---- .github/workflows/build_and_test.yml | 11 +---- .github/workflows/build_impl.yml | 36 +++++++++++--- .github/workflows/test_impl.yml | 2 +- CMakeLists.txt | 1 + cmake/Settings.cmake | 8 ++++ conanfile.py | 5 ++ 9 files changed, 112 insertions(+), 27 deletions(-) diff --git a/.github/actions/generate/action.yml b/.github/actions/generate/action.yml index 6c0c017b..bab12d3b 100644 --- a/.github/actions/generate/action.yml +++ b/.github/actions/generate/action.yml @@ -34,6 +34,10 @@ inputs: - "tsan" - "asan" - "ubsan" + time_trace: + description: Whether to enable compiler trace reports + required: true + default: "false" runs: using: composite @@ -49,6 +53,7 @@ runs: CODE_COVERAGE: "${{ inputs.code_coverage == 'true' && 'True' || 'False' }}" STATIC_OPTION: "${{ inputs.static == 'true' && 'True' || 'False' }}" INTEGRATION_TESTS_OPTION: "${{ inputs.build_integration_tests == 'true' && 'True' || 'False' }}" + TIME_TRACE: "${{ inputs.time_trace == 'true' && 'True' || 'False' }}" run: | cd build conan \ @@ -61,6 +66,7 @@ runs: -o clio:integration_tests="${INTEGRATION_TESTS_OPTION}" \ -o clio:lint=False \ -o clio:coverage="${CODE_COVERAGE}" \ + -o clio:time_trace="${TIME_TRACE}" \ --profile ${{ inputs.conan_profile }} - name: Run cmake diff --git a/.github/actions/prepare_runner/action.yml b/.github/actions/prepare_runner/action.yml index 65bf6166..066082e0 100644 --- a/.github/actions/prepare_runner/action.yml +++ b/.github/actions/prepare_runner/action.yml @@ -13,7 +13,17 @@ runs: if: ${{ runner.os == 'macOS' }} shell: bash run: | - brew install llvm@14 pkg-config ninja bison ccache jq gh conan@1 ca-certificates + brew install \ + bison \ + ca-certificates \ + ccache \ + clang-build-analyzer \ + conan@1 \ + gh \ + jq \ + llvm@14 \ + ninja \ + pkg-config echo "/opt/homebrew/opt/conan@1/bin" >> $GITHUB_PATH - name: Install CMake 3.31.6 on mac diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 62dc932e..ddef1058 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,21 +23,13 @@ jobs: conan_profile: [gcc, clang] build_type: [Release, Debug] container: ['{ "image": "ghcr.io/xrplf/clio-ci:latest" }'] - code_coverage: [false] static: [true] include: - - os: heavy - conan_profile: gcc - build_type: Debug - container: '{ "image": "ghcr.io/xrplf/clio-ci:latest" }' - code_coverage: true - static: true - os: macos15 conan_profile: default_apple_clang build_type: Release container: "" - code_coverage: false static: false uses: ./.github/workflows/build_and_test.yml @@ -46,12 +38,60 @@ jobs: container: ${{ matrix.container }} conan_profile: ${{ matrix.conan_profile }} build_type: ${{ matrix.build_type }} - code_coverage: ${{ matrix.code_coverage }} static: ${{ matrix.static }} run_unit_tests: true run_integration_tests: false upload_clio_server: true + code_coverage: + name: Run Code Coverage + + uses: ./.github/workflows/build_impl.yml + with: + runs_on: heavy + container: '{ "image": "ghcr.io/xrplf/clio-ci:latest" }' + conan_profile: gcc + build_type: Debug + disable_cache: false + code_coverage: true + static: true + upload_clio_server: false + targets: all + sanitizer: "false" + analyze_build_time: false + + analyze_build_time: + name: Analyze Build Time + + strategy: + fail-fast: false + matrix: + include: + # TODO: Enable when we have at least ubuntu 22.04 + # as ClangBuildAnalyzer requires relatively modern glibc + # + # - os: heavy + # conan_profile: clang + # container: '{ "image": "ghcr.io/xrplf/clio-ci:latest" }' + # static: true + - os: macos15 + conan_profile: default_apple_clang + container: "" + static: false + uses: ./.github/workflows/build_impl.yml + with: + runs_on: ${{ matrix.os }} + container: ${{ matrix.container }} + conan_profile: ${{ matrix.conan_profile }} + build_type: Release + disable_cache: true + code_coverage: false + static: ${{ matrix.static }} + upload_clio_server: false + targets: all + sanitizer: "false" + analyze_build_time: true + check_config: name: Check Config Description needs: build-and-test diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index e2853845..6a73e62f 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -29,12 +29,6 @@ on: type: boolean default: false - code_coverage: - description: Whether to enable code coverage - required: true - type: boolean - default: false - static: description: Whether to build static binaries required: true @@ -78,16 +72,15 @@ jobs: conan_profile: ${{ inputs.conan_profile }} build_type: ${{ inputs.build_type }} disable_cache: ${{ inputs.disable_cache }} - code_coverage: ${{ inputs.code_coverage }} + code_coverage: false static: ${{ inputs.static }} upload_clio_server: ${{ inputs.upload_clio_server }} targets: ${{ inputs.targets }} sanitizer: ${{ inputs.sanitizer }} + analyze_build_time: false test: needs: build - # We don't upload tests if code coverage is enabled - if: ${{ !inputs.code_coverage }} uses: ./.github/workflows/test_impl.yml with: runs_on: ${{ inputs.runs_on }} diff --git a/.github/workflows/build_impl.yml b/.github/workflows/build_impl.yml index 8bc95459..7f47fe7f 100644 --- a/.github/workflows/build_impl.yml +++ b/.github/workflows/build_impl.yml @@ -45,14 +45,19 @@ on: targets: description: Space-separated build target names - required: false + required: true type: string sanitizer: description: Sanitizer to use - required: false + required: true type: string + analyze_build_time: + description: Whether to enable build time analysis + required: true + type: boolean + jobs: build: name: Build ${{ inputs.container != '' && 'in container' || 'natively' }} @@ -98,12 +103,29 @@ jobs: code_coverage: ${{ inputs.code_coverage }} static: ${{ inputs.static }} sanitizer: ${{ inputs.sanitizer }} + time_trace: ${{ inputs.analyze_build_time }} - name: Build Clio uses: ./.github/actions/build_clio with: targets: ${{ inputs.targets }} + - name: Show build time analyze report + if: ${{ inputs.analyze_build_time }} + run: | + ClangBuildAnalyzer --all build/ build_time_report.bin + ClangBuildAnalyzer --analyze build_time_report.bin > build_time_report.txt + # Running again to have colorized output + ClangBuildAnalyzer --analyze build_time_report.bin + shell: bash + + - name: Upload build time analyze report + if: ${{ inputs.analyze_build_time }} + uses: actions/upload-artifact@v4 + with: + name: build_time_report_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} + path: build_time_report.txt + - name: Show ccache's statistics if: ${{ !inputs.disable_cache }} shell: bash @@ -115,29 +137,29 @@ jobs: cat /tmp/ccache.stats - name: Strip unit_tests - if: inputs.sanitizer == 'false' && !inputs.code_coverage + if: inputs.sanitizer == 'false' && !inputs.code_coverage && !inputs.analyze_build_time run: strip build/clio_tests - name: Strip integration_tests - if: inputs.sanitizer == 'false' && !inputs.code_coverage + if: inputs.sanitizer == 'false' && !inputs.code_coverage && !inputs.analyze_build_time run: strip build/clio_integration_tests - name: Upload clio_server - if: inputs.upload_clio_server && !inputs.code_coverage + if: inputs.upload_clio_server && !inputs.code_coverage && !inputs.analyze_build_time uses: actions/upload-artifact@v4 with: name: clio_server_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} path: build/clio_server - name: Upload clio_tests - if: ${{ !inputs.code_coverage }} + if: ${{ !inputs.code_coverage && !inputs.analyze_build_time }} uses: actions/upload-artifact@v4 with: name: clio_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} path: build/clio_tests - name: Upload clio_integration_tests - if: ${{ !inputs.code_coverage }} + if: ${{ !inputs.code_coverage && !inputs.analyze_build_time }} uses: actions/upload-artifact@v4 with: name: clio_integration_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} diff --git a/.github/workflows/test_impl.yml b/.github/workflows/test_impl.yml index 856458ee..d81a1e78 100644 --- a/.github/workflows/test_impl.yml +++ b/.github/workflows/test_impl.yml @@ -35,7 +35,7 @@ on: sanitizer: description: Sanitizer to use - required: false + required: true type: string jobs: diff --git a/CMakeLists.txt b/CMakeLists.txt index 27b1c3d5..0e2c62f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ option(packaging "Create distribution packages" FALSE) option(lint "Run clang-tidy checks during compilation" FALSE) option(static "Statically linked Clio" FALSE) option(snapshot "Build snapshot tool" FALSE) +option(time_trace "Build using -ftime-trace to create compiler trace reports" FALSE) # ========================================================================== # set(san "" CACHE STRING "Add sanitizer instrumentation") diff --git a/cmake/Settings.cmake b/cmake/Settings.cmake index 6ed74527..508a111c 100644 --- a/cmake/Settings.cmake +++ b/cmake/Settings.cmake @@ -70,4 +70,12 @@ endif () # See https://github.com/cpp-best-practices/cppbestpractices/blob/master/02-Use_the_Tools_Available.md#gcc--clang for # the flags description +if (time_trace) + if (is_clang OR is_appleclang) + list(APPEND COMPILER_FLAGS -ftime-trace) + else () + message(FATAL_ERROR "Clang or AppleClang is required to use `-ftime-trace`") + endif () +endif () + target_compile_options(clio_options INTERFACE ${COMPILER_FLAGS}) diff --git a/conanfile.py b/conanfile.py index 6d97d1af..d718e575 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,6 +1,7 @@ from conan import ConanFile from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout + class Clio(ConanFile): name = 'clio' license = 'ISC' @@ -20,6 +21,7 @@ class Clio(ConanFile): 'coverage': [True, False], # build for test coverage report; create custom target `clio_tests-ccov` 'lint': [True, False], # run clang-tidy checks during compilation 'snapshot': [True, False], # build export/import snapshot tool + 'time_trace': [True, False] # build using -ftime-trace to create compiler trace reports } requires = [ @@ -46,6 +48,7 @@ class Clio(ConanFile): 'lint': False, 'docs': False, 'snapshot': False, + 'time_trace': False, 'xrpl/*:tests': False, 'xrpl/*:rocksdb': False, @@ -83,6 +86,7 @@ class Clio(ConanFile): self.folders.generators = 'build/generators' generators = 'CMakeDeps' + def generate(self): tc = CMakeToolchain(self) tc.variables['verbose'] = self.options.verbose @@ -95,6 +99,7 @@ class Clio(ConanFile): tc.variables['packaging'] = self.options.packaging tc.variables['benchmark'] = self.options.benchmark tc.variables['snapshot'] = self.options.snapshot + tc.variables['time_trace'] = self.options.time_trace tc.generate() def build(self):