diff --git a/Documentation/AdvancedBuildInstructions.md b/Documentation/AdvancedBuildInstructions.md index 948e7b72e8c..5a5bcdc9e81 100644 --- a/Documentation/AdvancedBuildInstructions.md +++ b/Documentation/AdvancedBuildInstructions.md @@ -43,6 +43,7 @@ directory to `Build/i686` and then running `ninja `: There are some optional features that can be enabled during compilation that are intended to help with specific types of development work or introduce experimental features. Currently, the following build options are available: - `ENABLE_ADDRESS_SANITIZER` and `ENABLE_KERNEL_ADDRESS_SANITIZER`: builds in runtime checks for memory corruption bugs (like buffer overflows and memory leaks) in Lagom test cases and the kernel, respectively. - `ENABLE_KERNEL_COVERAGE_COLLECTION`: enables the KCOV API and kernel coverage collection instrumentation. Only useful for coverage guided kernel fuzzing. +- `ENABLE_USERSPACE_COVERAGE_COLLECTION`: enables coverage collection instrumentation for userspace. Currently only works with a Clang build. - `ENABLE_MEMORY_SANITIZER`: enables runtime checks for uninitialized memory accesses in Lagom test cases. - `ENABLE_UNDEFINED_SANITIZER`: builds in runtime checks for [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) (like null pointer dereferences and signed integer overflows) in Lagom test cases. - `ENABLE_COMPILER_EXPLORER_BUILD`: Skip building non-library entities in Lagom (this only applies to Lagom). diff --git a/Meta/CMake/serenity_options.cmake b/Meta/CMake/serenity_options.cmake index a6128368523..0d670337e7d 100644 --- a/Meta/CMake/serenity_options.cmake +++ b/Meta/CMake/serenity_options.cmake @@ -12,3 +12,4 @@ serenity_option(ENABLE_KERNEL_COVERAGE_COLLECTION OFF CACHE BOOL "Enable KCOV a serenity_option(ENABLE_KERNEL_LTO OFF CACHE BOOL "Build the kernel with link-time optimization") serenity_option(ENABLE_EXTRA_KERNEL_DEBUG_SYMBOLS OFF CACHE BOOL "Enable -Og and -ggdb3 options for Kernel code for easier debugging") serenity_option(ENABLE_MOLD_LINKER OFF CACHE BOOL "Link the SerenityOS userland with the mold linker") +serenity_option(ENABLE_USERSPACE_COVERAGE_COLLECTION OFF CACHE BOOL "Enable code coverage instrumentation for userspace binaries in clang") diff --git a/Meta/CMake/utils.cmake b/Meta/CMake/utils.cmake index 576de933464..1bfc3b8ea2b 100644 --- a/Meta/CMake/utils.cmake +++ b/Meta/CMake/utils.cmake @@ -73,6 +73,9 @@ function(serenity_libc target_name fs_name) if (NOT ENABLE_MOLD_LINKER) target_link_options(${target_name} PRIVATE -Wl,--no-dependent-libraries) endif() + if (ENABLE_USERSPACE_COVERAGE_COLLECTION) + target_link_libraries(${target_name} clang_rt.profile) + endif() endif() target_link_directories(LibC PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) serenity_generated_sources(${target_name}) diff --git a/Userland/CMakeLists.txt b/Userland/CMakeLists.txt index d062ff52de8..c3d2b295b04 100644 --- a/Userland/CMakeLists.txt +++ b/Userland/CMakeLists.txt @@ -1,5 +1,21 @@ add_compile_options(-O2) +# Escape hatch target to prevent runtime startup libraries from having coverage enabled +# at awkward points in program initialization +add_library(NoCoverage INTERFACE) + +if (ENABLE_USERSPACE_COVERAGE_COLLECTION) + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang$") + add_compile_options(-fprofile-instr-generate -fcoverage-mapping) + add_link_options(-fprofile-instr-generate -fcoverage-mapping) + + target_compile_options(NoCoverage INTERFACE -fno-profile-generate -fno-profile-instr-use -fno-profile-instr-generate -fno-coverage-mapping) + target_link_options(NoCoverage INTERFACE -fno-profile-generate -fno-profile-instr-use -fno-profile-instr-generate -fno-coverage-mapping) + else() + message(FATAL_ERROR "ENABLE_USERSPACE_COVERAGE_COLLECTION not supported yet for ${CMAKE_CXX_COMPILER_ID}") + endif() +endif() + add_subdirectory(Applications) add_subdirectory(Demos) add_subdirectory(DevTools) diff --git a/Userland/DynamicLoader/CMakeLists.txt b/Userland/DynamicLoader/CMakeLists.txt index fb86c2413b5..eaaf9b075ee 100644 --- a/Userland/DynamicLoader/CMakeLists.txt +++ b/Userland/DynamicLoader/CMakeLists.txt @@ -44,6 +44,7 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang$") target_link_libraries(Loader.so PRIVATE clang_rt.builtins) endif () -target_link_libraries(Loader.so PRIVATE LibTimeZone) +# Note: Don't confuse the coverage results by instrumenting Loader +target_link_libraries(Loader.so PRIVATE LibTimeZone NoCoverage) target_link_options(Loader.so PRIVATE LINKER:--no-dynamic-linker) install(TARGETS Loader.so RUNTIME DESTINATION usr/lib/) diff --git a/Userland/Libraries/LibC/CMakeLists.txt b/Userland/Libraries/LibC/CMakeLists.txt index eaad9350540..ad7dc31b3c1 100644 --- a/Userland/Libraries/LibC/CMakeLists.txt +++ b/Userland/Libraries/LibC/CMakeLists.txt @@ -93,24 +93,32 @@ endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option") +# Note: We link all these against NoCoverage so that we don't break ports by requiring coverage symbols +# in runtime/startup objects. +# Since all native serenity applications use dynamic libraries, prevent coverage on libc.a as well + add_library(crt0 STATIC crt0.cpp) +target_link_libraries(crt0 PRIVATE NoCoverage) add_custom_command( TARGET crt0 COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_INSTALL_PREFIX}/usr/lib/crt0.o ) add_library(crt0_shared STATIC crt0_shared.cpp) +target_link_libraries(crt0_shared PRIVATE NoCoverage) add_custom_command( TARGET crt0_shared COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_INSTALL_PREFIX}/usr/lib/crt0_shared.o ) add_library(crti STATIC ${CRTI_SOURCE}) +target_link_libraries(crti PRIVATE NoCoverage) add_custom_command( TARGET crti COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_INSTALL_PREFIX}/usr/lib/crti.o ) add_library(crtn STATIC ${CRTN_SOURCE}) +target_link_libraries(crtn PRIVATE NoCoverage) add_custom_command( TARGET crtn COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_INSTALL_PREFIX}/usr/lib/crtn.o @@ -119,6 +127,7 @@ add_custom_command( set_source_files_properties (ssp.cpp PROPERTIES COMPILE_FLAGS "-fno-stack-protector") add_library(ssp STATIC ssp.cpp) +target_link_libraries(ssp PRIVATE NoCoverage) add_custom_command( TARGET ssp COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_INSTALL_PREFIX}/usr/lib/ssp.o @@ -131,7 +140,7 @@ set(SOURCES ${LIBC_SOURCES} ${AK_SOURCES} ${ELF_SOURCES} ${ASM_SOURCES}) set_source_files_properties(stdio.cpp PROPERTIES COMPILE_FLAGS "-fno-builtin-fputc -fno-builtin-fputs -fno-builtin-fwrite") add_library(LibCStaticWithoutDeps STATIC ${SOURCES}) -target_link_libraries(LibCStaticWithoutDeps ssp LibTimeZone) +target_link_libraries(LibCStaticWithoutDeps PUBLIC ssp LibTimeZone PRIVATE NoCoverage) add_dependencies(LibCStaticWithoutDeps LibM LibSystem LibUBSanitizer) add_custom_target(LibCStatic diff --git a/Userland/Libraries/LibSanitizer/CMakeLists.txt b/Userland/Libraries/LibSanitizer/CMakeLists.txt index 0666db50f5f..cf13d1f531a 100644 --- a/Userland/Libraries/LibSanitizer/CMakeLists.txt +++ b/Userland/Libraries/LibSanitizer/CMakeLists.txt @@ -10,3 +10,4 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib") serenity_libc(LibUBSanitizer ubsan) add_library(LibUBSanitizerStatic STATIC ${SOURCES}) +target_link_libraries(LibUBSanitizerStatic PRIVATE NoCoverage) diff --git a/Userland/Libraries/LibSystem/CMakeLists.txt b/Userland/Libraries/LibSystem/CMakeLists.txt index 61ffa0622f6..a8932887751 100644 --- a/Userland/Libraries/LibSystem/CMakeLists.txt +++ b/Userland/Libraries/LibSystem/CMakeLists.txt @@ -8,3 +8,4 @@ target_include_directories(LibSystem PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) add_library(LibSystemStatic STATIC ${SOURCES}) target_include_directories(LibSystemStatic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(LibSystemStatic PRIVATE NoCoverage) diff --git a/Userland/Libraries/LibTimeZone/CMakeLists.txt b/Userland/Libraries/LibTimeZone/CMakeLists.txt index 2b2dd4b43d2..b885df29f3a 100644 --- a/Userland/Libraries/LibTimeZone/CMakeLists.txt +++ b/Userland/Libraries/LibTimeZone/CMakeLists.txt @@ -7,3 +7,7 @@ set(SOURCES add_library(LibTimeZone OBJECT ${SOURCES}) target_compile_definitions(LibTimeZone PRIVATE ENABLE_TIME_ZONE_DATA=$) + +# NOTE: These objects are used by the DynamicLoader, which is always built without coverage instrumentation. +# We could allow them to be instrumented for coverage if DynamicLoader built its own copy +target_link_libraries(LibTimeZone PRIVATE NoCoverage)