Skip to content

Commit 6c1b437

Browse files
committed
Improved code-coverage parallelization
An issue with LLVM-based code coverage has been the fact that, if run in parallel, multiple targets could be be writing to the binaries.list and/or profraw.list files simultaneously, leading to undefined results, or on Windows the command failing due to the file being used by another process entirely. To avoid this, each target will now write to separate target-specific files in the coverage data directories. Then when the data is actually required, a few `cat` commands will run collecting all the items into single files for further use.
1 parent d0cbe69 commit 6c1b437

File tree

1 file changed

+55
-35
lines changed

1 file changed

+55
-35
lines changed

code-coverage.cmake

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH)
108108
# Variables
109109
set(CMAKE_COVERAGE_DATA_DIRECTORY ${CMAKE_BINARY_DIR}/ccov-data)
110110
set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov)
111-
set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1)
112111

113112
# Common initialization/checks
114113
if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED)
@@ -121,6 +120,13 @@ if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED)
121120
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
122121
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
123122

123+
# to create lists of linked objects to avoid multi-process file handle
124+
# issues
125+
file(MAKE_DIRECTORY ${CMAKE_COVERAGE_DATA_DIRECTORY}/objects)
126+
# to create lists of raw profile data while avoiding multi-process file
127+
# handle issues
128+
file(MAKE_DIRECTORY ${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw)
129+
124130
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang" OR CMAKE_CXX_COMPILER_ID
125131
MATCHES "AppleClang")
126132
# When on macOS and using the Apple-provided toolchain, use the
@@ -156,17 +162,17 @@ if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED)
156162
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
157163
add_custom_target(
158164
ccov-clean
159-
COMMAND ${CMAKE_COMMAND} -E remove -f
160-
${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list
161-
COMMAND ${CMAKE_COMMAND} -E remove -f
162-
${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw.list)
165+
COMMAND
166+
${CMAKE_COMMAND} -E remove -f #
167+
${CMAKE_COVERAGE_DATA_DIRECTORY}/objects/*
168+
${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw/*)
163169
else()
164170
add_custom_target(
165171
ccov-clean
166-
COMMAND ${CMAKE_COMMAND} -E rm -f
167-
${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list
168-
COMMAND ${CMAKE_COMMAND} -E rm -f
169-
${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw.list)
172+
COMMAND
173+
${CMAKE_COMMAND} -E rm -f #
174+
${CMAKE_COVERAGE_DATA_DIRECTORY}/objects/*
175+
${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw/*)
170176
endif()
171177

172178
# Used to get the shared object file list before doing the main all-
@@ -329,8 +335,9 @@ function(target_code_coverage TARGET_NAME)
329335
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
330336
add_custom_target(
331337
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
332-
COMMAND ${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>"
333-
>> ${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list
338+
COMMAND
339+
${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>" >
340+
${CMAKE_COVERAGE_DATA_DIRECTORY}/objects/${target_code_coverage_COVERAGE_TARGET_NAME}
334341
DEPENDS ${TARGET_NAME})
335342

336343
if(NOT TARGET ccov-libs)
@@ -376,12 +383,13 @@ function(target_code_coverage TARGET_NAME)
376383
$<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
377384
COMMAND
378385
${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>"
379-
${LINKED_OBJECTS} >> ${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list
386+
${LINKED_OBJECTS} >
387+
${CMAKE_COVERAGE_DATA_DIRECTORY}/objects/$${target_code_coverage_COVERAGE_TARGET_NAME}
380388
COMMAND
381389
${CMAKE_COMMAND} -E echo
382390
"${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw"
383-
>> ${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw.list
384-
JOB_POOL ccov_serial_pool
391+
>
392+
${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw/${target_code_coverage_COVERAGE_TARGET_NAME}
385393
DEPENDS ccov-libs ${TARGET_NAME})
386394

387395
# Merge the generated profile data so llvm-cov can process it
@@ -604,22 +612,34 @@ function(add_code_coverage_all_targets)
604612
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
605613
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
606614

607-
# Merge the profile data for all of the run executables
615+
# Merge the profile data for all of the run targets
608616
if(WIN32)
609617
add_custom_target(
610618
ccov-all-processing
619+
COMMAND powershell cat ${CMAKE_COVERAGE_DATA_DIRECTORY}/objects/* >
620+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-objects.list
621+
COMMAND powershell cat ${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw/* >
622+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-profraw.list
611623
COMMAND
612624
powershell -Command $$FILELIST = Get-Content
613-
${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw.list\; llvm-profdata.exe
614-
merge -o ${CMAKE_COVERAGE_DATA_DIRECTORY}/all-merged.profdata -sparse
615-
$$FILELIST)
625+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-profraw.list \;
626+
llvm-profdata.exe merge -o
627+
${CMAKE_COVERAGE_DATA_DIRECTORY}/ccov-all.profdata -sparse $$FILELIST)
616628
else()
617629
add_custom_target(
618630
ccov-all-processing
631+
COMMAND
632+
${CMAKE_COMMAND} -E cat
633+
${CMAKE_COVERAGE_DATA_DIRECTORY}/objects/* >
634+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-objects.list
635+
COMMAND
636+
${CMAKE_COMMAND} -E cat
637+
${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw/* >
638+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-profraw.list
619639
COMMAND
620640
${LLVM_PROFDATA_PATH} merge -o
621-
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-merged.profdata -sparse `cat
622-
${CMAKE_COVERAGE_DATA_DIRECTORY}/profraw.list`)
641+
${CMAKE_COVERAGE_DATA_DIRECTORY}/ccov-all.profdata -sparse `cat
642+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-profraw.list`)
623643
endif()
624644

625645
# Regex exclude only available for LLVM >= 7
@@ -636,18 +656,18 @@ function(add_code_coverage_all_targets)
636656
ccov-all-report
637657
COMMAND
638658
powershell -Command $$FILELIST = Get-Content
639-
${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list\; llvm-cov.exe report
640-
$$FILELIST
641-
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/all-merged.profdata
659+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-objects.list \; llvm-cov.exe
660+
report $$FILELIST
661+
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/ccov-all.profdata
642662
${EXCLUDE_REGEX}
643663
DEPENDS ccov-all-processing)
644664
else()
645665
add_custom_target(
646666
ccov-all-report
647667
COMMAND
648668
${LLVM_COV_PATH} report `cat
649-
${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list`
650-
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/all-merged.profdata
669+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-objects.list`
670+
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/ccov-all.profdata
651671
${EXCLUDE_REGEX}
652672
DEPENDS ccov-all-processing)
653673
endif()
@@ -659,9 +679,9 @@ function(add_code_coverage_all_targets)
659679
ccov-all-export
660680
COMMAND
661681
powershell -Command $$FILELIST = Get-Content
662-
${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list\; llvm-cov.exe export
663-
$$FILELIST
664-
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/all-merged.profdata
682+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-objects.list \; llvm-cov.exe
683+
export $$FILELIST
684+
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/ccov-all.profdata
665685
-format="text" ${EXCLUDE_REGEX} >
666686
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json
667687
DEPENDS ccov-all-processing)
@@ -670,8 +690,8 @@ function(add_code_coverage_all_targets)
670690
ccov-all-export
671691
COMMAND
672692
${LLVM_COV_PATH} export `cat
673-
${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list`
674-
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/all-merged.profdata
693+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-objects.list`
694+
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/ccov-all.profdata
675695
-format="text" ${EXCLUDE_REGEX} >
676696
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json
677697
DEPENDS ccov-all-processing)
@@ -683,9 +703,9 @@ function(add_code_coverage_all_targets)
683703
ccov-all
684704
COMMAND
685705
powershell -Command $$FILELIST = Get-Content
686-
${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list\; llvm-cov.exe show
706+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-objects.list \; llvm-cov.exe show
687707
$$FILELIST
688-
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/all-merged.profdata
708+
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/ccov-all.profdata
689709
-show-line-counts-or-regions
690710
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
691711
-format="html" ${EXCLUDE_REGEX}
@@ -695,8 +715,8 @@ function(add_code_coverage_all_targets)
695715
ccov-all
696716
COMMAND
697717
${LLVM_COV_PATH} show `cat
698-
${CMAKE_COVERAGE_DATA_DIRECTORY}/binaries.list`
699-
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/all-merged.profdata
718+
${CMAKE_COVERAGE_DATA_DIRECTORY}/all-objects.list`
719+
-instr-profile=${CMAKE_COVERAGE_DATA_DIRECTORY}/ccov-all.profdata
700720
-show-line-counts-or-regions
701721
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
702722
-format="html" ${EXCLUDE_REGEX}
@@ -705,7 +725,7 @@ function(add_code_coverage_all_targets)
705725

706726
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
707727
"GNU")
708-
set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info")
728+
set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/ccov-all.info")
709729

710730
# Nothing required for gcov
711731
add_custom_target(ccov-all-processing COMMAND ;)

0 commit comments

Comments
 (0)