diff --git a/README.md b/README.md index a7da1da..319be02 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ This is a collection of quite useful scripts that expand the possibilities for b - [1b - Via target commands](#1b---via-target-commands) - [Example 2: Target instrumented, but with regex pattern of files to be excluded from report](#example-2-target-instrumented-but-with-regex-pattern-of-files-to-be-excluded-from-report) - [Example 3: Target added to the 'ccov' and 'ccov-all' targets](#example-3-target-added-to-the-ccov-and-ccov-all-targets) + - [Example 4: Hook all targets](#example-4-hook-all-targets) - [AFL Fuzzing Instrumentation `afl-fuzzing.cmake`](#afl-fuzzing-instrumentation-afl-fuzzingcmake) - [Usage](#usage-1) - [Compiler Options `compiler-options.cmake`](#compiler-options-compiler-optionscmake) @@ -136,6 +137,7 @@ To enable any code coverage instrumentation/targets, the single CMake option of From this point, there are two primary methods for adding instrumentation to targets: 1. A blanket instrumentation by calling `add_code_coverage()`, where all targets in that directory and all subdirectories are automatically instrumented. 2. Per-target instrumentation by calling `target_code_coverage()`, where the target is given and thus only that target is instrumented. This applies to both libraries and executables. +3. Automatically add coverage for each target with `-DCCOV_TARGETS_HOOK=On` and `-DCCOV_TARGETS_HOOK_ARGS=...` for default values, requires `add_code_coverage()` or similar. To add coverage targets, such as calling `make ccov` to generate the actual coverage information for perusal or consumption, call `target_code_coverage()` on an *executable* target. @@ -184,6 +186,20 @@ add_executable(theExe main.cpp non_covered.cpp) target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder. ``` +#### Example 4: Hook all targets +``` +# this could be as well command line argument +set(CCOV_TARGETS_HOOK ON) # enable 'add_executable' and 'add_library' hooks +set(CCOV_TARGETS_HOOK_ARGS ALL AUTO) # set default arguments for coverage + +add_code_coverage() # Adds instrumentation to all targets + +add_library(theLib lib.cpp) # ccov-theLib target will be add + +add_executable(theExe main.cpp) # ccov-theExe target will be add +target_link_libraries(theExe PRIVATE theLib) +``` + ## AFL Fuzzing Instrumentation [`afl-fuzzing.cmake`](afl-fuzzing.cmake) > American fuzzy lop is a security-oriented fuzzer that employs a novel type of compile-time instrumentation and genetic algorithms to automatically discover clean, interesting test cases that trigger new internal states in the targeted binary. This substantially improves the functional coverage for the fuzzed code. The compact synthesized corpora produced by the tool are also useful for seeding other, more labor- or resource-intensive testing regimes down the road. diff --git a/code-coverage.cmake b/code-coverage.cmake index be8ab06..9f16273 100644 --- a/code-coverage.cmake +++ b/code-coverage.cmake @@ -72,6 +72,20 @@ # add_executable(theExe main.cpp non_covered.cpp) # target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder. # ~~~ +# +# Example 4: Hook all targets +# +# ~~~ +# set(CCOV_TARGETS_HOOK ON) # enable 'add_executable' and 'add_library' hooks +# set(CCOV_TARGETS_HOOK_ARGS ALL AUTO) # set default arguments for coverage +# +# add_code_coverage() # Adds instrumentation to all targets +# +# add_library(theLib lib.cpp) # ccov-theLib target will be add +# +# add_executable(theExe main.cpp) # ccov-theExe target will be add +# target_link_libraries(theExe PRIVATE theLib) +# ~~~ # Options option( @@ -79,6 +93,14 @@ option( "Builds targets with code coverage instrumentation. (Requires GCC or Clang)" OFF) +option( + CCOV_TARGETS_HOOK + "Autocapture all new targets." + OFF) + +set(CCOV_TARGETS_HOOK_ARGS "" CACHE STRING + "Default arguments for all hooked targets.") + # Programs find_program(LLVM_COV_PATH llvm-cov) find_program(LLVM_PROFDATA_PATH llvm-profdata) @@ -181,6 +203,47 @@ if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED) else() message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.") endif() + + if (CCOV_TARGETS_HOOK) + if (COMMAND _add_executable) + message(FATAL_ERROR "add_executable was already redefined. Only one redefinitions is allowed.") + endif() + + set(CCOV_TARGETS_HOOK_LIST ${CCOV_TARGETS_HOOK_ARGS}) + separate_arguments(CCOV_TARGETS_HOOK_LIST) + + macro(add_executable target_name) + _add_executable(${target_name} ${ARGN}) + if (${ARGC} GREATER 1) + string(TOUPPER ${ARGV1} TARGET_TYPE) + endif() + string(COMPARE NOTEQUAL "${TARGET_TYPE}" IMPORTED IS_NOT_IMPORTED) + + if (IS_NOT_IMPORTED) + target_code_coverage(${target_name} ${CCOV_TARGETS_HOOK_LIST}) + endif() + endmacro(add_executable) + + if (COMMAND _add_library) + message(FATAL_ERROR "add_library was already redefined. Only one redefinitions is allowed.") + endif() + macro(add_library target_name) + _add_library(${target_name} ${ARGN}) + if (${ARGC} GREATER 1) + string(TOUPPER "${ARGV1}" TARGET_TYPE) + endif() + if (${ARGC} GREATER 2) + string(TOUPPER "${ARGV2}" IMPORTED_TYPE) + endif() + string(COMPARE NOTEQUAL "${TARGET_TYPE}" ALIAS IS_NOT_ALIAS) + string(COMPARE NOTEQUAL "${IMPORTED_TYPE}" IMPORTED IS_NOT_IMPORTED) + + if (IS_NOT_ALIAS AND IS_NOT_IMPORTED) + target_code_coverage(${target_name} ${CCOV_TARGETS_HOOK_LIST}) + endif() + endmacro(add_library) + endif (CCOV_TARGETS_HOOK) + endif() # Adds code coverage instrumentation to a library, or instrumentation/targets @@ -208,6 +271,7 @@ endif() # Optional: # PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE. # INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE. +# PLAIN - Do not set any target visibility (backward compatibility with old cmake projects) # AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets. # ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets. # EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory @@ -218,7 +282,7 @@ endif() # ~~~ function(target_code_coverage TARGET_NAME) # Argument parsing - set(options AUTO ALL EXTERNAL PUBLIC INTERFACE) + set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN) set(single_value_keywords COVERAGE_TARGET_NAME) set(multi_value_keywords EXCLUDE OBJECTS ARGS) cmake_parse_arguments( @@ -229,10 +293,16 @@ function(target_code_coverage TARGET_NAME) # PRIVATE. if(target_code_coverage_PUBLIC) set(TARGET_VISIBILITY PUBLIC) + set(TARGET_LINK_VISIBILITY PUBLIC) elseif(target_code_coverage_INTERFACE) set(TARGET_VISIBILITY INTERFACE) + set(TARGET_LINK_VISIBILITY INTERFACE) + elseif(target_code_coverage_PLAIN) + set(TARGET_VISIBILITY PUBLIC) + set(TARGET_LINK_VISIBILITY) else() set(TARGET_VISIBILITY PRIVATE) + set(TARGET_LINK_VISIBILITY PRIVATE) endif() if(NOT target_code_coverage_COVERAGE_TARGET_NAME) @@ -253,7 +323,7 @@ function(target_code_coverage TARGET_NAME) "GNU") target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs -ftest-coverage) - target_link_libraries(${TARGET_NAME} ${TARGET_VISIBILITY} gcov) + target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov) endif() # Targets diff --git a/example/code-coverage-hook/CMakeLists.txt b/example/code-coverage-hook/CMakeLists.txt new file mode 100644 index 0000000..e5972b5 --- /dev/null +++ b/example/code-coverage-hook/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.10) +project(code-coverage-hook C CXX) + +# Set the searching location for cmake 'include' locations +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../..;") +# Include the code coverage module +cmake_policy(SET CMP0077 NEW) + +set(CCOV_TARGETS_HOOK ON) +set(CCOV_TARGETS_HOOK_ARGS "ALL") + +include(code-coverage) + +# Require C++11 +include(c++-standards) +cxx_11() +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# This introduces the 'ccov-all' targets Also excludes the main file via a regex +add_code_coverage_all_targets(EXCLUDE coverage.main.cpp) + +# The library +add_library(lib ../src/coverage.cpp) + +# The executable +add_executable(main ../src/coverage.main.cpp) +target_link_libraries(main PUBLIC lib) + +# The second executable +add_executable(main2 ../src/coverage.main.cpp) +target_link_libraries(main2 PUBLIC lib)