Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

coverage tool support #107

Merged
merged 10 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,31 @@ build --copt="-Wall"
build --copt="-Werror"
build:windows --copt=/wd4716

# Pass PATH variable from the environment
build --action_env=PATH

# Common flags for Clang
build:clang --action_env=BAZEL_COMPILER=clang
build:clang --action_env=CC=clang --action_env=CXX=clang++
build:clang --linkopt=-fuse-ld=lld

# Coverage options
coverage --config=coverage
coverage --build_tests_only
build:coverage --config=clang
build:coverage --action_env=BAZEL_USE_LLVM_NATIVE_COVERAGE=1
build:coverage --action_env=GCOV=llvm-profdata
build:coverage --combined_report=lcov
build:coverage --experimental_use_llvm_covmap
build:coverage --experimental_generate_llvm_lcov
build:coverage --collect_code_coverage
build:coverage --instrumentation_filter="//source[/:],//cpp2sky[/:]"
build:coverage --coverage_support=@cpp2sky//bazel/coverage:coverage_support
build:coverage --test_env=CC_CODE_COVERAGE_SCRIPT=external/cpp2sky/bazel/coverage/collect_cc_coverage.sh
build:coverage --strategy=TestRunner=local
build:coverage --strategy=CoverageReport=local
build:coverage --experimental_use_llvm_covmap
build:coverage --collect_code_coverage
build:coverage --test_tag_filters=-nocoverage

try-import %workspace%/user.bazelrc
2 changes: 1 addition & 1 deletion .bazelversion
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.7.2
4.0.0
74 changes: 53 additions & 21 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,72 @@ on:
pull_request:
branches: [ main ]

env:
BAZEL_LINK: https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64
CLANG_LINK: https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.1/clang+llvm-10.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz

jobs:
format:
runs-on: ubuntu-latest
steps:
- run: |
echo "/opt/llvm/bin" >> $GITHUB_PATH
- uses: actions/checkout@v3
- name: Setup clang-format
run: |
sudo wget -O /tmp/clang-llvm.tar.xz $CLANG_LINK
sudo mkdir -p /opt/llvm
sudo tar -xf /tmp/clang-llvm.tar.xz -C /opt/llvm --strip-components 1
git clone https://github.com/Sarcasm/run-clang-format.git
- name: Run clang-format
run: find ./ -iname "*.h" -o -iname "*.cc" | xargs ./run-clang-format/run-clang-format.py

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: |
echo "/opt/llvm/bin" >> $GITHUB_PATH
- uses: actions/checkout@v3
- name: Install Bazel
run: |
sudo wget -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64
sudo wget -O /usr/local/bin/bazel $BAZEL_LINK
sudo chmod +x /usr/local/bin/bazel
- name: Run bazel test with c++11
- name: Install Clang
run: |
sudo wget -O /tmp/clang-llvm.tar.xz $CLANG_LINK
sudo mkdir -p /opt/llvm
sudo tar -xf /tmp/clang-llvm.tar.xz -C /opt/llvm --strip-components 1
- name: Run bazel test with GCC c++11
run: |
bazel test --cxxopt=-std=c++0x //...
- name: Run bazel test with c++17
- name: Run bazel test with GCC c++17
run: |
bazel test --cxxopt=-std=c++17 //...

format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Setup clang-format
run: |
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.1/clang+llvm-10.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz
tar -xvf clang+llvm-10.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz
sudo mv ./clang+llvm-10.0.1-x86_64-linux-gnu-ubuntu-16.04/bin/clang-format /usr/local/bin
rm -rf clang+llvm-10.0.1-x86_64-linux-gnu-ubuntu-16.04/
git clone https://github.com/Sarcasm/run-clang-format.git
- name: Run clang-format
run: find ./ -iname "*.h" -o -iname "*.cc" | xargs ./run-clang-format/run-clang-format.py
bazel test --cxxopt=-std=c++17 //...
- name: Run bazel test with CLANG c++11
run: |
bazel test --config=clang --cxxopt=-std=c++0x //...
- name: Run bazel test with CLANG c++17
run: |
bazel test --config=clang --cxxopt=-std=c++17 //...
- name: Install lcov and genhtml
run: |
sudo apt update
sudo apt -y install lcov
- name: Run coverage test
run: |
./coverage.sh
- name: upload coverage data to codecov
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true
files: ./coverage_report/coverage.dat
name: codecov-cpp2sky
verbose: true

e2e-cpp:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Prepare service container
run: |
docker-compose -f test/e2e/docker/docker-compose.e2e.yml up -d
Expand All @@ -55,7 +87,7 @@ jobs:
e2e-python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Prepare service container
run: |
docker-compose -f test/e2e/docker/docker-compose.e2e-python.yml up -d
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ user.bazelrc
.vscode/

.clangd/
compile_commands.json
compile_commands.json

coverage_report
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# cpp2sky

![cpp2sky test](https://github.com/SkyAPM/cpp2sky/workflows/cpp2sky%20test/badge.svg)
![codecov test](https://codecov.io/gh/SkyAPM/cpp2sky/branch/main/graph/badge.svg)

Distributed tracing and monitor SDK in CPP for Apache SkyWalking APM. This SDK is compatible with C++ 17, C++ 14, and C++ 11.

Expand Down
6 changes: 6 additions & 0 deletions bazel/coverage/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
licenses(["notice"]) # Apache 2

filegroup(
name = "coverage_support",
srcs = ["collect_cc_coverage.sh"],
)
208 changes: 208 additions & 0 deletions bazel/coverage/collect_cc_coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#!/bin/bash -x
# This is fork from https://raw.githubusercontent.com/bazelbuild/bazel/master/tools/test/collect_cc_coverage.sh

# Copyright 2016 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This script collects code coverage data for C++ sources, after the tests
# were executed.
#
# Bazel C++ code coverage collection support is poor and limited. There is
# an ongoing effort to improve this (tracking issue #1118).
#
# Bazel uses the lcov tool for gathering coverage data. There is also
# an experimental support for clang llvm coverage, which uses the .profraw
# data files to compute the coverage report.
#
# This script assumes the following environment variables are set:
# - COVERAGE_DIR Directory containing metadata files needed for
# coverage collection (e.g. gcda files, profraw).
# - COVERAGE_MANIFEST Location of the instrumented file manifest.
# - COVERAGE_GCOV_PATH Location of gcov. This is set by the TestRunner.
# - COVERAGE_GCOV_OPTIONS Additional options to pass to gcov.
# - ROOT Location from where the code coverage collection
# was invoked.
#
# The script looks in $COVERAGE_DIR for the C++ metadata coverage files (either
# gcda or profraw) and uses either lcov or gcov to get the coverage data.
# The coverage data is placed in $COVERAGE_OUTPUT_FILE.

# Checks if clang llvm coverage should be used instead of lcov.
function uses_llvm() {
if stat "${COVERAGE_DIR}"/*.profraw >/dev/null 2>&1; then
return 0
fi
return 1
}

# Returns 0 if gcov must be used, 1 otherwise.
function uses_gcov() {
[[ "$GCOV_COVERAGE" -eq "1" ]] && return 0
return 1
}

function init_gcov() {
# Symlink the gcov tool such with a link called gcov. Clang comes with a tool
# called llvm-cov, which behaves like gcov if symlinked in this way (otherwise
# we would need to invoke it with "llvm-cov gcov").
# For more details see https://llvm.org/docs/CommandGuide/llvm-cov.html.
GCOV="${COVERAGE_DIR}/gcov"
if [ ! -f "${COVERAGE_GCOV_PATH}" ]; then
echo "GCov does not exist at the given path: '${COVERAGE_GCOV_PATH}'"
exit 1
fi
# When using a tool from a toolchain COVERAGE_GCOV_PATH will be a relative
# path. To make it work on different working directories it's required to
# convert the path to an absolute one.
COVERAGE_GCOV_PATH_ABS="$(cd "${COVERAGE_GCOV_PATH%/*}" && pwd)/${COVERAGE_GCOV_PATH##*/}"
ln -s "${COVERAGE_GCOV_PATH_ABS}" "${GCOV}"
}

# Computes code coverage data using the clang generated metadata found under
# $COVERAGE_DIR.
# Writes the collected coverage into the given output file.
function llvm_coverage_lcov() {
local output_file="${1}"; shift
export LLVM_PROFILE_FILE="${COVERAGE_DIR}/%h-%p-%m.profraw"
"${COVERAGE_GCOV_PATH}" merge -output "${output_file}.data" \
"${COVERAGE_DIR}"/*.profraw

local object_param=""
while read -r line; do
if [[ ${line: -24} == "runtime_objects_list.txt" ]]; then
while read -r line_runtime_object; do
if [[ ${line_runtime_object} == *"absl"* ]]; then
continue
fi
object_param+=" -object ${RUNFILES_DIR}/${TEST_WORKSPACE}/${line_runtime_object}"
done < "${line}"
fi
done < "${COVERAGE_MANIFEST}"

"${LLVM_COV}" export -instr-profile "${output_file}.data" -format=lcov \
-ignore-filename-regex='.*external/.+' \
-ignore-filename-regex='/tmp/.+' \
${object_param} | sed 's#/proc/self/cwd/##' > "${output_file}"
}

function llvm_coverage_profdata() {
local output_file="${1}"; shift
export LLVM_PROFILE_FILE="${COVERAGE_DIR}/%h-%p-%m.profraw"
"${COVERAGE_GCOV_PATH}" merge -output "${output_file}" \
"${COVERAGE_DIR}"/*.profraw
}

# Generates a code coverage report in gcov intermediate text format by invoking
# gcov and using the profile data (.gcda) and notes (.gcno) files.
#
# The profile data files are expected to be found under $COVERAGE_DIR.
# The notes file are expected to be found under $ROOT.
#
# - output_file The location of the file where the generated code coverage
# report is written.
function gcov_coverage() {
local output_file="${1}"; shift

# We'll save the standard output of each the gcov command in this log.
local gcov_log="$output_file.gcov.log"

# Copy .gcno files next to their corresponding .gcda files in $COVERAGE_DIR
# because gcov expects them to be in the same directory.
while read -r line; do
if [[ ${line: -4} == "gcno" ]]; then
gcno_path=${line}
local gcda="${COVERAGE_DIR}/$(dirname ${gcno_path})/$(basename ${gcno_path} .gcno).gcda"
# If the gcda file was not found we skip generating coverage from the gcno
# file.
if [[ -f "$gcda" ]]; then
# gcov expects both gcno and gcda files to be in the same directory.
# We overcome this by copying the gcno to $COVERAGE_DIR where the gcda
# files are expected to be.
if [ ! -f "${COVERAGE_DIR}/${gcno_path}" ]; then
mkdir -p "${COVERAGE_DIR}/$(dirname ${gcno_path})"
cp "$ROOT/${gcno_path}" "${COVERAGE_DIR}/${gcno_path}"
fi
# Invoke gcov to generate a code coverage report with the flags:
# -i Output gcov file in an intermediate text format.
# The output is a single .gcov file per .gcda file.
# No source code is required.
# -o directory The directory containing the .gcno and
# .gcda data files.
# "${gcda"} The input file name. gcov is looking for data files
# named after the input filename without its extension.
# gcov produces files called <source file name>.gcov in the current
# directory. These contain the coverage information of the source file
# they correspond to. One .gcov file is produced for each source
# (or header) file containing code which was compiled to produce the
# .gcda files.
# Don't generate branch coverage (-b) because of a gcov issue that
# segfaults when both -i and -b are used (see
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84879).
"${GCOV}" -i $COVERAGE_GCOV_OPTIONS -o "$(dirname ${gcda})" "${gcda}"

# Extract gcov's version: the output of `gcov --version` contains the
# version as a set of major-minor-patch numbers, of which we extract
# the major version.
gcov_major_version=$("${GCOV}" --version | sed -n -E -e 's/^.*\s([0-9]+)\.[0-9]+\.[0-9]+\s?.*$/\1/p')

# Check the gcov version so we can process the data correctly
if [[ $gcov_major_version -ge 9 ]]; then
# gcov 9 or higher use a JSON based format for their coverage reports.
# The output is generated into multiple files: "$(basename ${gcda}).gcov.json.gz"
# Concatenating JSON documents does not yield a valid document, so they are moved individually
mv -- *.gcov.json.gz "$(dirname "$output_file")"
else
# Append all .gcov files in the current directory to the output file.
cat -- *.gcov >> "$output_file"
# Delete the .gcov files.
rm -- *.gcov
fi
fi
fi
done < "${COVERAGE_MANIFEST}"
}

function main() {
init_gcov

# If llvm code coverage is used, we output the raw code coverage report in
# the $COVERAGE_OUTPUT_FILE. This report will not be converted to any other
# format by LcovMerger.
# TODO(#5881): Convert profdata reports to lcov.
if uses_llvm; then
if [[ "${GENERATE_LLVM_LCOV}" == "1" ]]; then
BAZEL_CC_COVERAGE_TOOL="LLVM_LCOV"
else
BAZEL_CC_COVERAGE_TOOL="PROFDATA"
fi
fi

# When using either gcov or lcov, have an output file specific to the test
# and format used. For lcov we generate a ".dat" output file and for gcov
# a ".gcov" output file. It is important that these files are generated under
# COVERAGE_DIR.
# When this script is invoked by tools/test/collect_coverage.sh either of
# these two coverage reports will be picked up by LcovMerger and their
# content will be converted and/or merged with other reports to an lcov
# format, generating the final code coverage report.
case "$BAZEL_CC_COVERAGE_TOOL" in
("GCOV") gcov_coverage "$COVERAGE_DIR/_cc_coverage.gcov" ;;
("PROFDATA") llvm_coverage_profdata "$COVERAGE_DIR/_cc_coverage.profdata" ;;
("LLVM_LCOV") llvm_coverage_lcov "$COVERAGE_DIR/_cc_coverage.dat" ;;
(*) echo "Coverage tool $BAZEL_CC_COVERAGE_TOOL not supported" \
&& exit 1
esac
}

main
Loading