From de840db17a44f18328323eb841a237765088f2f9 Mon Sep 17 00:00:00 2001 From: Krzysztof Naglik Date: Wed, 26 Oct 2022 02:38:50 -0700 Subject: [PATCH] Fix identical gcov json file name problem **The problem:** Bazel moves all `.gcov.json.gz` files to one directory. If in a test target, two source files have identical names, then the second `.gcov.json.gz` overwrites the first one. **The solution:** I added `gcno_path` to the move destination in order to distinguish multiple `.gcov.json.gz` with the same name. **Testing:** In the `test_cc_test_coverage_gcov` test case I added the `different/a.cc` source file, so currently we have the following source tree: ``` coverage_srcs/a.h coverage_srcs/a.cc coverage_srcs/b.h coverage_srcs/t.cc coverage_srcs/different/a.h coverage_srcs/different/a.cc ``` gcda and gcno files are created next to the source files. The final `gcov.json` files are placed in the corresponding paths: ``` $COVERAGE_DIR_VAR/coverage_srcs/*a.gcov.json.gz $COVERAGE_DIR_VAR/coverage_srcs/*t.gcov.json.gz $COVERAGE_DIR_VAR/coverage_srcs/different/*a.gcov.json.gz ``` Closes #16527. PiperOrigin-RevId: 483911427 Change-Id: I1608407e4b7264fb5fd436997bdc344344932b97 --- .../bazel/bazel_cc_code_coverage_test.sh | 139 +++++++++++------- tools/test/collect_cc_coverage.sh | 2 +- 2 files changed, 89 insertions(+), 52 deletions(-) diff --git a/src/test/shell/bazel/bazel_cc_code_coverage_test.sh b/src/test/shell/bazel/bazel_cc_code_coverage_test.sh index 3748bae5cbc57f..6177856d1d9dcd 100755 --- a/src/test/shell/bazel/bazel_cc_code_coverage_test.sh +++ b/src/test/shell/bazel/bazel_cc_code_coverage_test.sh @@ -70,6 +70,7 @@ function set_up() { # Create the CC sources. mkdir -p "$ROOT_VAR/coverage_srcs/" + mkdir -p "$ROOT_VAR/coverage_srcs/different" cat << EOF > "$ROOT_VAR/coverage_srcs/a.h" int a(bool what); EOF @@ -85,6 +86,20 @@ int a(bool what) { return b(-1); } } +EOF + + cat << EOF > "$ROOT_VAR/coverage_srcs/different/a.h" +int different_a(bool what); +EOF + + cat << EOF > "$ROOT_VAR/coverage_srcs/different/a.cc" +int different_a(bool what) { + if (what) { + return 1; + } else { + return 2; + } +} EOF cat << EOF > "$ROOT_VAR/coverage_srcs/b.h" @@ -100,37 +115,24 @@ EOF cat << EOF > "$ROOT_VAR/coverage_srcs/t.cc" #include #include "a.h" +#include "different/a.h" int main(void) { a(true); + different_a(false); } EOF generate_and_execute_instrumented_binary coverage_srcs/test \ - coverage_srcs/a.h coverage_srcs/a.cc \ - coverage_srcs/b.h \ - coverage_srcs/t.cc - - # Prior to version 11, g++ generates the notes files in the current directory - # instead of next to the object file despite the documentation indicating otherwise: - # https://gcc.gnu.org/onlinedocs/gcc/Gcov-Data-Files.html#Gcov-Data-Files - # This is fixed in g++ 11 so we have to handle both cases. - - local not_found=0 - ls coverage_srcs/*a.gcno > /dev/null 2>&1 || not_found=$? - if [[ $not_found -ne 0 ]]; then - agcno=$(ls *a.gcno) - tgcno=$(ls *t.gcno) - agcda=$(ls *a.gcda) - tgcda=$(ls *t.gcda) - mv $agcno coverage_srcs/$agcno - mv $tgcno coverage_srcs/$tgcno - mv $agcda coverage_srcs/$agcda - mv $tgcda coverage_srcs/$tgcda - fi + coverage_srcs/a.cc coverage_srcs/a.o \ + coverage_srcs/different/a.cc coverage_srcs/different/a.o \ + coverage_srcs/t.cc coverage_srcs/t.o + agcno=$(ls coverage_srcs/*a.gcno) + dagcno=$(ls coverage_srcs/different/*a.gcno) tgcno=$(ls coverage_srcs/*t.gcno) agcda=$(ls coverage_srcs/*a.gcda) + dagcda=$(ls coverage_srcs/different/*a.gcda) tgcda=$(ls coverage_srcs/*t.gcda) # Even though gcov expects the gcda files to be next to the gcno files, # during Bazel execution this will not be the case. collect_cc_coverage.sh @@ -140,32 +142,41 @@ EOF # https://github.com/bazelbuild/bazel/issues/16229 mv $agcda "$COVERAGE_DIR_VAR/$agcda" mv $tgcda "$COVERAGE_DIR_VAR/$tgcda" + mkdir "$COVERAGE_DIR_VAR/$(dirname ${dagcda})" + mv $dagcda "$COVERAGE_DIR_VAR/$dagcda" # All generated .gcno files need to be in the manifest otherwise # the coverage report will be incomplete. echo "$tgcno" >> "$COVERAGE_MANIFEST_VAR" echo "$agcno" >> "$COVERAGE_MANIFEST_VAR" + echo "$dagcno" >> "$COVERAGE_MANIFEST_VAR" } # Generates and executes an instrumented binary: # -# Reads the list of arguments provided by the caller (using $@) and uses them -# to produce an instrumented binary using g++. This step also generates -# the notes (.gcno) files. +# Reads the list of source files along with object files paths. Uses them +# to produce object files and link them to an instrumented binary using g++. +# This step also generates the notes (.gcno) files. # # Executes the instrumented binary. This step also generates the # profile data (.gcda) files. +# +# - [(source_file, object_file),...] - source files and object file paths # - path_to_binary destination of the binary produced by g++ function generate_and_execute_instrumented_binary() { local path_to_binary="${1}"; shift - g++ -coverage \ - "$@" -o "$path_to_binary" \ - || fail "Couldn't produce the instrumented binary for $@ \ - with path_to_binary $path_to_binary" - - # Execute the instrumented binary and generates the profile data (.gcda) - # file. - # The profile data file is placed in $gcda_directory. + local src_file="" + local obj_file="" + local obj_files="" + while [[ $# -ge 2 ]]; do + src_file=$1; shift + obj_file=$1; shift + obj_files="${obj_files} $obj_file" + g++ -coverage -fprofile-arcs -ftest-coverage -lgcov -c \ + "$src_file" -o "$obj_file" + done + + g++ -coverage -fprofile-arcs -ftest-coverage -lgcov -o "$path_to_binary" $obj_files "$path_to_binary" || fail "Couldn't execute the instrumented binary \ $path_to_binary" } @@ -223,11 +234,12 @@ function assert_gcov_coverage_srcs_t_cc() { local output_file="${1}"; shift # The expected coverage result for coverage_srcs/t.cc in gcov format. - local expected_gcov_result_t_cc="file:coverage_srcs/t.cc -function:4,1,main -lcount:4,1 + local expected_gcov_result_t_cc="coverage_srcs/t.cc +function:5,1,main lcount:5,1 -lcount:6,1" +lcount:6,1 +lcount:7,1 +lcount:8,1" assert_coverage_entry_in_file "$expected_gcov_result_t_cc" "$output_file" } @@ -244,6 +256,19 @@ lcount:5,0" assert_coverage_entry_in_file "$expected_gcov_result" "$output_file" } +function assert_gcov_coverage_srcs_d_a_cc() { + local output_file="${1}"; shift + + # The expected coverage result for coverage_srcs/different/a.cc in gcov format. + local expected_gcov_result_d_a_cc="file:coverage_srcs/different/a.cc +function:1,1,_Z11different_ab +lcount:1,1 +lcount:2,1 +lcount:3,0 +lcount:5,1" + assert_coverage_entry_in_file "$expected_gcov_result_d_a_cc" "$output_file" +} + function assert_gcov_coverage_srcs_a_cc_json() { local output_file="${1}"; shift @@ -265,7 +290,7 @@ function assert_gcov_coverage_srcs_t_cc_json() { # The expected coverage result for coverage_srcs/t.cc in gcov format. cat > expected_gcov_result_t_cc < expected_gcov_result_b_h < expected_gcov_result_d_a_cc < /dev/null 2>&1 || not_found=$? + ls $COVERAGE_DIR_VAR/coverage_srcs/*.gcda.gcov.json.gz > /dev/null 2>&1 || not_found=$? if [[ $not_found -ne 0 ]]; then - agcda=$(ls $COVERAGE_DIR_VAR/*a.gcov.json.gz) - tgcda=$(ls $COVERAGE_DIR_VAR/*t.gcov.json.gz) + agcda=$(ls $COVERAGE_DIR_VAR/coverage_srcs/*a.gcov.json.gz) + tgcda=$(ls $COVERAGE_DIR_VAR/coverage_srcs/*t.gcov.json.gz) + dagcda=$(ls $COVERAGE_DIR_VAR/coverage_srcs/different/*a.gcov.json.gz) else - agcda=$(ls $COVERAGE_DIR_VAR/*a.gcda.gcov.json.gz) - tgcda=$(ls $COVERAGE_DIR_VAR/*t.gcda.gcov.json.gz) + agcda=$(ls $COVERAGE_DIR_VAR/coverage_srcs/*a.gcda.gcov.json.gz) + tgcda=$(ls $COVERAGE_DIR_VAR/coverage_srcs/*t.gcda.gcov.json.gz) + dagcda=$(ls $COVERAGE_DIR_VAR/coverage_srcs/different/*a.gcda.gcov.json.gz) fi output_file_json="output_file.json" - zcat $agcda $tgcda > $output_file_json + zcat $agcda $tgcda $dagcda > $output_file_json assert_gcov_coverage_srcs_a_cc_json "$output_file_json" assert_gcov_coverage_srcs_t_cc_json "$output_file_json" assert_gcov_coverage_srcs_b_h_json "$output_file_json" + assert_gcov_coverage_srcs_d_a_cc_json "$output_file_json" local nr_files="$(grep -o -i "\"file\":" "$output_file_json" | wc -l)" - [[ "$nr_files" == 3 ]] || \ + [[ "$nr_files" == 4 ]] || \ fail "Number of files in C++ gcov coverage output file is "\ - "$nr_files and different than 3" + "$nr_files and different than 4" fi } diff --git a/tools/test/collect_cc_coverage.sh b/tools/test/collect_cc_coverage.sh index f532e9be31a86e..eafa7b8fe8f1ce 100755 --- a/tools/test/collect_cc_coverage.sh +++ b/tools/test/collect_cc_coverage.sh @@ -163,7 +163,7 @@ function gcov_coverage() { # 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")" + mv -- *.gcov.json.gz "$(dirname "$output_file")/$(dirname ${gcno_path})" else # Append all .gcov files in the current directory to the output file. cat -- *.gcov >> "$output_file"