diff --git a/bazel/BUILD b/bazel/BUILD index c288f351054fd..3964708254862 100755 --- a/bazel/BUILD +++ b/bazel/BUILD @@ -85,6 +85,11 @@ config_setting( values = {"define": "ENVOY_CONFIG_COVERAGE=1"}, ) +config_setting( + name = "coverage_llvm_build", + values = {"define": "ENVOY_CONFIG_COVERAGE=llvm"}, +) + config_setting( name = "disable_tcmalloc", values = {"define": "tcmalloc=disabled"}, diff --git a/bazel/envoy_binary.bzl b/bazel/envoy_binary.bzl index a7a369e8c16be..27ac5f6a5ea5e 100644 --- a/bazel/envoy_binary.bzl +++ b/bazel/envoy_binary.bzl @@ -67,6 +67,12 @@ def _envoy_linkopts(): "-ldl", "-Wl,--hash-style=gnu", ], + }) + select({ + "@envoy//bazel:coverage_llvm_build": [ + "-fprofile-instr-generate", + "-fcoverage-mapping", + ], + "//conditions:default": [], }) + envoy_static_link_libstdcpp_linkopts() + \ _envoy_select_exported_symbols(["-Wl,-E"]) diff --git a/bazel/envoy_library.bzl b/bazel/envoy_library.bzl index 33e9e93fc0491..cc3f00755a014 100644 --- a/bazel/envoy_library.bzl +++ b/bazel/envoy_library.bzl @@ -47,7 +47,13 @@ def envoy_cc_library( name = name, srcs = srcs, hdrs = hdrs, - copts = envoy_copts(repository) + copts, + copts = envoy_copts(repository) + copts + select({ + repository + "//bazel:coverage_llvm_build": [ + "-fprofile-instr-generate", + "-fcoverage-mapping", + ], + "//conditions:default": [], + }), visibility = visibility, tags = tags, textual_hdrs = textual_hdrs, diff --git a/bazel/envoy_test.bzl b/bazel/envoy_test.bzl index ea564d619fa83..b521feb29eae7 100644 --- a/bazel/envoy_test.bzl +++ b/bazel/envoy_test.bzl @@ -60,6 +60,12 @@ def _envoy_test_linkopts(): # TODO(mattklein123): It's not great that we universally link against the following libs. # In particular, -latomic and -lrt are not needed on all platforms. Make this more granular. "//conditions:default": ["-pthread", "-lrt", "-ldl"], + }) + select({ + "@envoy//bazel:coverage_llvm_build": [ + "-fprofile-instr-generate", + "-fcoverage-mapping", + ], + "//conditions:default": [], }) + envoy_select_force_libcpp([], ["-lstdc++fs", "-latomic"]) # Envoy C++ fuzz test targets. These are not included in coverage runs. diff --git a/ci/do_ci.sh b/ci/do_ci.sh index ec4d05496390f..877bc252188ec 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -241,24 +241,13 @@ elif [[ "$CI_TARGET" == "bazel.api" ]]; then @envoy_api//tools:tap2pcap_test exit 0 elif [[ "$CI_TARGET" == "bazel.coverage" ]]; then - setup_gcc_toolchain + setup_clang_toolchain echo "bazel coverage build with tests ${TEST_TARGETS}" - # gcovr is a pain to run with `bazel run`, so package it up into a - # relocatable and hermetic-ish .par file. - bazel build --python_version=PY2 @com_github_gcovr_gcovr//:gcovr.par - export GCOVR="/tmp/gcovr.par" - cp -f "${ENVOY_SRCDIR}/bazel-bin/external/com_github_gcovr_gcovr/gcovr.par" ${GCOVR} - - # Reduce the amount of memory and number of cores Bazel tries to use to - # prevent it from launching too many subprocesses. This should prevent the - # system from running out of memory and killing tasks. See discussion on - # https://github.com/envoyproxy/envoy/pull/5611. - # TODO(akonradi): use --local_cpu_resources flag once Bazel has a release - # after 0.21. - [ -z "$CIRCLECI" ] || export BAZEL_BUILD_OPTIONS="${BAZEL_BUILD_OPTIONS} --local_resources=12288,4,1" + # LLVM coverage is a memory hog too. + [ -z "$CIRCLECI" ] || export BAZEL_BUILD_OPTIONS="${BAZEL_BUILD_OPTIONS} --local_cpu_resources=6" - test/run_envoy_bazel_coverage.sh ${TEST_TARGETS} + test/run_envoy_bazel_llvm_coverage.sh ${TEST_TARGETS} collect_build_profile coverage exit 0 elif [[ "$CI_TARGET" == "bazel.clang_tidy" ]]; then diff --git a/test/run_envoy_bazel_llvm_coverage.sh b/test/run_envoy_bazel_llvm_coverage.sh new file mode 100755 index 0000000000000..26b68b1e90b17 --- /dev/null +++ b/test/run_envoy_bazel_llvm_coverage.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -e + +[[ -z "${SRCDIR}" ]] && SRCDIR="${PWD}" +[[ -z "${BAZEL_COVERAGE}" ]] && BAZEL_COVERAGE=bazel + +echo "Starting run_envoy_bazel_coverage.sh..." +echo " PWD=$(pwd)" +echo " SRCDIR=${SRCDIR}" +echo " BAZEL_COVERAGE=${BAZEL_COVERAGE}" +echo " VALIDATE_COVERAGE=${VALIDATE_COVERAGE}" + +# This is the target that will be run to generate coverage data. It can be overridden by consumer +# projects that want to run coverage on a different/combined target. +# Command-line arguments take precedence over ${COVERAGE_TARGET}. +if [[ $# -gt 0 ]]; then + COVERAGE_TARGETS=$* +elif [[ -n "${COVERAGE_TARGET}" ]]; then + COVERAGE_TARGETS=${COVERAGE_TARGET} +else + COVERAGE_TARGETS="//test/... @com_googlesource_quiche//:all" +fi + +rm -rf $(find -L bazel-bin -name "test-*.profraw") + +"${BAZEL_COVERAGE}" test "${COVERAGE_TARGETS}" ${BAZEL_BUILD_OPTIONS} -c fastbuild --copt=-DNDEBUG \ + --cache_test_results=no --define ENVOY_CONFIG_COVERAGE=llvm --cxxopt="-DENVOY_CONFIG_COVERAGE=1" \ + --test_output=all --strategy=TestRunner=local --test_filter='-QuicPlatformTest.QuicStackTraceTest' \ + --test_env=LLVM_PROFILE_FILE=test-%p.profraw --test_arg="--log-path /dev/null" --test_arg="-l trace" + +COVERAGE_DIR="${SRCDIR}"/generated/coverage +mkdir -p "${COVERAGE_DIR}" + +echo "Merging profile data..." +llvm-profdata merge -sparse $(find -L bazel-bin -name "test-*.profraw") -o ${COVERAGE_DIR}/coverage.profdata + +echo "Generating report..." +llvm-cov show bazel-bin/source/exe/envoy-static -instr-profile=${COVERAGE_DIR}/coverage.profdata \ + -ignore-filename-regex='(/external/|pb\.(validate\.)?(h|cc)|/chromium_url/)' -output-dir=${COVERAGE_DIR} -format=html +sed -i -e 's|>bazel-out/[^/]*/bin/\([^/]*\)/[^<]*/_virtual_includes/[^/]*|>\1|g' "${COVERAGE_DIR}/index.html" + +[[ -z "${ENVOY_COVERAGE_DIR}" ]] || rsync -av "${COVERAGE_DIR}"/ "${ENVOY_COVERAGE_DIR}"