From d9eddf1d496950e5ed18a49452037fdd1246172f Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Thu, 30 Jan 2025 16:24:00 +0200 Subject: [PATCH] CI: Run sanitizers in docker (#580) --- .github/workflows/sanitize.yaml | 31 +++++---- docker/build_deps.sh | 17 ++++- docker/sanitizers/Dockerfile | 23 +++++++ docker/sanitizers/README.md | 25 +++++++ .../sanitizers/build_sanitizer.sh | 66 ++++++++++--------- .../sanitizers/clang-libcxx-sanitizer.cmake | 0 .../sanitizers/sanitizers.json | 0 .../sanitizers/toolchain64.cmake | 0 8 files changed, 116 insertions(+), 46 deletions(-) create mode 100644 docker/sanitizers/Dockerfile create mode 100644 docker/sanitizers/README.md rename {.github/workflows => docker}/sanitizers/build_sanitizer.sh (87%) rename {.github/workflows => docker}/sanitizers/clang-libcxx-sanitizer.cmake (100%) rename {.github/workflows => docker}/sanitizers/sanitizers.json (100%) rename {.github/workflows => docker}/sanitizers/toolchain64.cmake (100%) diff --git a/.github/workflows/sanitize.yaml b/.github/workflows/sanitize.yaml index d5b4a299c3..2483f9f508 100644 --- a/.github/workflows/sanitize.yaml +++ b/.github/workflows/sanitize.yaml @@ -9,26 +9,31 @@ concurrency: cancel-in-progress: true jobs: - get_dependencies: - name: "Dependencies" - if: github.event.review.state == 'APPROVED' - uses: ./.github/workflows/dependencies.yaml - build_and_run_sanitizer: name: ${{ matrix.mode }} - needs: get_dependencies strategy: matrix: mode: ["asan", "msan", "tsan", "ubsan"] fail-fast: false - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 + if: github.event.review.state == 'APPROVED' steps: - uses: actions/checkout@v4 - - uses: actions/cache/restore@v4 - with: - path: deps - key: ${{ needs.get_dependencies.outputs.cache_key }} + - name : Purge runner + # Strip the runner to avoid space quota exceeding + run: | + sudo apt-get purge -y \ + azure-cli microsoft-edge-stable google-cloud-cli \ + google-chrome-stable temurin-21-jdk temurin-17-jdk \ + temurin-11-jdk dotnet-sdk-8.0 firefox temurin-8-jdk \ + powershell libllvm17t64 libllvm18 libllvm16t64 \ + openjdk-21-jre-headless mysql-server-core-8.0 + sudo apt-get autoremove + sudo apt-get autoclean - name: Build [${{ matrix.mode }}] - run: ${{ github.workspace }}/.github/workflows/sanitizers/build_sanitizer.sh ${{ matrix.mode }} + run: | + docker build -f ${{ github.workspace }}/docker/sanitizers/Dockerfile \ + --no-cache --build-arg SANITIZER_NAME=${{ matrix.mode }} \ + -t sanitizer-${{ matrix.mode }} . - name: UT [c++,${{ matrix.mode }}] - run: ${{ github.workspace }}/cmake.bld/Linux/run-unittests.sh + run: docker run --rm sanitizer-${{ matrix.mode }} diff --git a/docker/build_deps.sh b/docker/build_deps.sh index 8dfeaae976..bcf5601b72 100755 --- a/docker/build_deps.sh +++ b/docker/build_deps.sh @@ -2,9 +2,22 @@ # This script downloads, builds, and installs the required build dependencies of BMQ # from github.com/bloomberg. Software packages are installed to the /opt/bb prefix. +# If the optional argument '--only-download' is provided, the script will only download +# dependencies (build and install steps are skipped). set -euxo pipefail +if [ $# == 1 ]; then + if [[ $1 == "--only-download" ]]; then + DO_BUILD=false + else + echo "Unexpected optional argument, only '--only-download' is supported" + exit 1 + fi +else + DO_BUILD=true +fi + fetch_git() { local org=$1 local repo=$2 @@ -68,4 +81,6 @@ build() { fetch_deps configure -build +if [ "${DO_BUILD}" = true ]; then + build +fi diff --git a/docker/sanitizers/Dockerfile b/docker/sanitizers/Dockerfile new file mode 100644 index 0000000000..b781481bcf --- /dev/null +++ b/docker/sanitizers/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1 + +FROM docker.io/ubuntu:24.04 + +# Check if SANITIZER_NAME argument is passed +ARG SANITIZER_NAME +RUN if [ -z $SANITIZER_NAME ]; then \ + echo "Required SANITIZER_NAME build argument is not passed." ;\ + exit 1 ;\ + fi + +# Copy blazingmq source code +WORKDIR /blazingmq +COPY docker docker +COPY etc etc +COPY src src +COPY CMakeLists.txt ./ + +# Build with sanitizer instrumentation +RUN docker/sanitizers/build_sanitizer.sh ${SANITIZER_NAME} + +# Run unit tests +CMD [ "/blazingmq/cmake.bld/Linux/run-unittests.sh" ] diff --git a/docker/sanitizers/README.md b/docker/sanitizers/README.md new file mode 100644 index 0000000000..7c871fb8d5 --- /dev/null +++ b/docker/sanitizers/README.md @@ -0,0 +1,25 @@ +# Purpose +This folder contains scripts to run BlazingMQ and its dependencies under sanitizer (asan, msan, tsan and ubsan) in Docker container. + +Usually sanitizers check is done on CI, but using Docker it is possible to run sanitizers check in both CI and local environment. + +## Running sanitizer check in local environment to debug sanitizer issues + - Prerequisites: docker should be installed; + - Run docker build from the BlazingMQ root folder: + ``` + docker build -f docker/sanitizers/Dockerfile --no-cache --build-arg SANITIZER_NAME= -t sanitizer- . + ``` + NOTE: `sanitizer-name` is `asan`, `msan`, `tsan` or `ubsan`. + +- Run docker container with unit tests +``` +docker run --rm sanitizer- +``` +NOTE: `sanitizer-name` - sanitizer's name from previous step. + +For debbugging, it is possible to run docker container with `/bin/bash` option and run desired tests manually, e.g. +``` +docker run --rm -it sanitizer- /bin/bash + +root@923efd7529a4:/blazingmq# cd cmake.bld/Linux && ./run-env.sh ctest -R +``` diff --git a/.github/workflows/sanitizers/build_sanitizer.sh b/docker/sanitizers/build_sanitizer.sh similarity index 87% rename from .github/workflows/sanitizers/build_sanitizer.sh rename to docker/sanitizers/build_sanitizer.sh index 1d80e39bd6..08fdbcd7b2 100755 --- a/.github/workflows/sanitizers/build_sanitizer.sh +++ b/docker/sanitizers/build_sanitizer.sh @@ -5,7 +5,7 @@ # - Clang # - LLVM libc++ standard library # - A CMake toolchain file specific for instrumented build -# It is currently used to build instrumented BlazingMQ binaries for CI for all +# It is used to build instrumented BlazingMQ binaries for all # Clang sanitizers (i.e. Address/Leak, Memory, Thread, UndefinedBehavior). # # It performs the following: @@ -18,11 +18,10 @@ # 6) Build sanitizer-instrumented BlazingMQ unit tests. # 7) Generate scripts to run unit tests: # ./cmake.bld/Linux/run-unittests.sh -# This script is used as-is by CI to run unit tests under sanitizer. set -eux -# :: Required arguments ::::::::::::::::::::::::::::::::::::::::::::::::::::::: +# Check required arguments if [ -z "${1}" ]; then echo 'Error: Missing sanitizer name.' >&2 echo ' (Usage: build_sanitizer.sh )' >&2 @@ -31,19 +30,11 @@ fi SANITIZER_NAME="${1}" -# Github's 'ubuntu-22.04' image contains a lot of preinstalled tools, -# see https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md. -# Uninstall uneeded tools which cause of versions clash. -sudo apt-get purge \ - llvm-14 \ - clang-14 \ - gcc-9 \ - gcc-10 \ - gcc-11 \ - gcc-12 - # Install prerequisites -sudo apt-get update && sudo apt-get install -qy \ +# Set up CA certificates first before installing other dependencies +apt-get update && \ +apt-get install -y ca-certificates && \ +apt-get install -qy --no-install-recommends \ lsb-release \ wget \ software-properties-common \ @@ -54,31 +45,33 @@ sudo apt-get update && sudo apt-get install -qy \ ninja-build \ bison \ libfl-dev \ - pkg-config + pkg-config \ + && rm -rf /var/lib/apt/lists/* -# Install prerequisites for LLVM: latest cmake version, Ubuntu apt repository contains cmake version 3.22.1 +# Install prerequisites for LLVM: latest cmake version, Ubuntu apt repository contains stale version wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null \ | gpg --dearmor - \ - | sudo tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null -sudo apt-add-repository -y "deb https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" -sudo apt-get install -qy cmake + | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null +apt-add-repository -y "deb https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" +apt-get install -qy cmake # Install LLVM wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh -LLVM_VERSION=17 -sudo ./llvm.sh ${LLVM_VERSION} all +LLVM_VERSION=18 +LLVM_TAG="llvmorg-18.1.8" +./llvm.sh ${LLVM_VERSION} all # Create version-agnostic pointers to required LLVM binaries. -sudo ln -sf /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang -sudo ln -sf /usr/bin/clang++-${LLVM_VERSION} /usr/bin/clang++ -sudo ln -sf /usr/bin/llvm-symbolizer-${LLVM_VERSION} /usr/bin/llvm-symbolizer +ln -sf /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang +ln -sf /usr/bin/clang++-${LLVM_VERSION} /usr/bin/clang++ +ln -sf /usr/bin/llvm-symbolizer-${LLVM_VERSION} /usr/bin/llvm-symbolizer # Set some initial constants PARALLELISM=8 DIR_ROOT="${PWD}" -DIR_SCRIPTS="${DIR_ROOT}/.github/workflows/sanitizers" +DIR_SCRIPTS="${DIR_ROOT}/docker/sanitizers" DIR_EXTERNAL="${DIR_ROOT}/deps" DIR_SRCS_EXT="${DIR_EXTERNAL}/srcs" DIR_BUILD_EXT="${DIR_EXTERNAL}/cmake.bld" @@ -111,24 +104,28 @@ github_url() { echo "https://github.com/$1.git"; } # Download external dependencies mkdir -p "${DIR_SRCS_EXT}" -# Download LLVM -LLVM_TAG="llvmorg-17.0.6" +# Download LLVM sources curl -SL "https://github.com/llvm/llvm-project/archive/refs/tags/${LLVM_TAG}.tar.gz" \ | tar -xzC "${DIR_SRCS_EXT}" mv "${DIR_SRCS_EXT}/llvm-project-${LLVM_TAG}" "${DIR_SRCS_EXT}/llvm-project" -# Download google-benchmark -GOOGLE_BENCHMARK_TAG="v1.8.4" +# Download google-benchmark sources +GOOGLE_BENCHMARK_TAG="v1.9.1" checkoutGitRepo "$(github_url google/benchmark)" "${GOOGLE_BENCHMARK_TAG}" "google-benchmark" -# Download googletest -GOOGLETEST_TAG="v1.14.0" +# Download googletest sources +GOOGLETEST_TAG="v1.15.2" checkoutGitRepo "$(github_url google/googletest)" "${GOOGLETEST_TAG}" "googletest" # Download zlib ZLIB_TAG="v1.3.1" checkoutGitRepo "$(github_url madler/zlib)" "${ZLIB_TAG}" "zlib" +# Download bde-tools, bde and ntf-core sources +cd "${DIR_EXTERNAL}" +"${DIR_ROOT}"/docker/build_deps.sh "--only-download" +cd - + # Build libc++ with required instrumentation # # The extent to which all dependencies to be compiled with sanitizer-support @@ -236,6 +233,11 @@ cmake -B "${DIR_SRCS_EXT}/zlib/cmake.bld" -S "${DIR_SRCS_EXT}/zlib" \ cmake --build "${DIR_SRCS_EXT}/zlib/cmake.bld" -j${PARALLELISM} cmake --install "${DIR_SRCS_EXT}/zlib/cmake.bld" +# Remove un-needed folders +rm -rf "${DIR_BUILD_EXT}" +rm -rf "${DIR_SRCS_EXT}/bde" +rm -rf "${DIR_SRCS_EXT}/ntf-core" + # Build BlazingMQ PKG_CONFIG_PATH="/opt/bb/lib64/pkgconfig:/opt/bb/lib/pkgconfig:/opt/bb/share/pkgconfig:$(pkg-config --variable pc_path pkg-config)" \ cmake -B "${DIR_BUILD_BMQ}" -S "${DIR_SRC_BMQ}" -G Ninja \ diff --git a/.github/workflows/sanitizers/clang-libcxx-sanitizer.cmake b/docker/sanitizers/clang-libcxx-sanitizer.cmake similarity index 100% rename from .github/workflows/sanitizers/clang-libcxx-sanitizer.cmake rename to docker/sanitizers/clang-libcxx-sanitizer.cmake diff --git a/.github/workflows/sanitizers/sanitizers.json b/docker/sanitizers/sanitizers.json similarity index 100% rename from .github/workflows/sanitizers/sanitizers.json rename to docker/sanitizers/sanitizers.json diff --git a/.github/workflows/sanitizers/toolchain64.cmake b/docker/sanitizers/toolchain64.cmake similarity index 100% rename from .github/workflows/sanitizers/toolchain64.cmake rename to docker/sanitizers/toolchain64.cmake