diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 1ebdf700578..08a0f88e428 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -140,7 +140,7 @@ ifeq ($(CONFIG_LIBM_TOOLCHAIN),y) STDLIBS += -lm endif -ifeq ($(CONFIG_SCHED_GCOV),y) +ifeq ($(CONFIG_COVERAGE_TOOLCHAIN),y) STDLIBS += -lgcov endif diff --git a/boards/sim/sim/sim/configs/kasan/defconfig b/boards/sim/sim/sim/configs/kasan/defconfig index 34b43ed9a20..369a239922a 100644 --- a/boards/sim/sim/sim/configs/kasan/defconfig +++ b/boards/sim/sim/sim/configs/kasan/defconfig @@ -13,6 +13,7 @@ CONFIG_ARCH_SIM=y CONFIG_BOARDCTL_POWEROFF=y CONFIG_BUILTIN=y CONFIG_CANCELLATION_POINTS=y +CONFIG_COVERAGE_TOOLCHAIN=y CONFIG_DEBUG_FEATURES=y CONFIG_DEBUG_SYMBOLS=y CONFIG_DEV_LOOP=y @@ -38,7 +39,6 @@ CONFIG_PTHREAD_MUTEX_TYPES=y CONFIG_PTHREAD_MUTEX_UNSAFE=y CONFIG_READLINE_TABCOMPLETION=y CONFIG_RR_INTERVAL=10 -CONFIG_SCHED_GCOV=y CONFIG_SCHED_HAVE_PARENT=y CONFIG_SCHED_LPWORK=y CONFIG_SCHED_WAITPID=y diff --git a/libs/libbuiltin/Kconfig b/libs/libbuiltin/Kconfig index fa92c8ee8f5..e5294112ce3 100644 --- a/libs/libbuiltin/Kconfig +++ b/libs/libbuiltin/Kconfig @@ -29,11 +29,38 @@ config BUILTIN_TOOLCHAIN endchoice +choice + prompt "Code coverage analysis support" + default COVERAGE_NONE + ---help--- + Select the code coverage analysis library + config COVERAGE_COMPILER_RT bool "Builtin libclang_rt.profile" select LIB_BUILTIN select LIB_COMPILER_RT - default n + ---help--- + Compile the LLVM Compiler-rt library into the OS. + Support adding native rich compilation options "-fprofile-xxx" + +config COVERAGE_MINI + bool "Builtin code coverage analysis mini library" + select LIB_BUILTIN + ---help--- + This is a mini version of the library, which is + used for code coverage analysis. If the toolchain + is clang, only support the compilation option + "-fprofile-instr-generate -fcoverage-mapping" + +config COVERAGE_TOOLCHAIN + bool "Link toolchain gcov library to the OS" + ---help--- + Link the toolchain coverage library to the OS. + +config COVERAGE_NONE + bool "Disable coverage function" + +endchoice choice prompt "Builtin profile support" diff --git a/libs/libbuiltin/Makefile b/libs/libbuiltin/Makefile index de3fb04984a..54350fbbcf7 100644 --- a/libs/libbuiltin/Makefile +++ b/libs/libbuiltin/Makefile @@ -26,9 +26,7 @@ BINDIR ?= bin KBIN = libkbuiltin$(LIBEXT) KBINDIR = kbin -ifeq ($(CONFIG_LIB_COMPILER_RT),y) include compiler-rt/Make.defs -endif include libgcc/Make.defs diff --git a/libs/libbuiltin/compiler-rt/CMakeLists.txt b/libs/libbuiltin/compiler-rt/CMakeLists.txt index cb66e68dae5..57c33df5c78 100644 --- a/libs/libbuiltin/compiler-rt/CMakeLists.txt +++ b/libs/libbuiltin/compiler-rt/CMakeLists.txt @@ -52,75 +52,77 @@ if(CONFIG_LIB_COMPILER_RT) endif() - if(CONFIG_ARCH_ARM) - set(ARCH arm) - elseif(CONFIG_ARCH_RISCV) - set(ARCH riscv) - elseif(CONFIG_ARCH_X86_64) - set(ARCH x86_64) - elseif(CONFIG_ARCH_ARM64) - set(ARCH aarch64) +endif() + +if(CONFIG_ARCH_ARM) + set(ARCH arm) +elseif(CONFIG_ARCH_RISCV) + set(ARCH riscv) +elseif(CONFIG_ARCH_X86_64) + set(ARCH x86_64) +elseif(CONFIG_ARCH_ARM64) + set(ARCH aarch64) +endif() + +list(APPEND INCDIR ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/include) + +if(CONFIG_BUILTIN_COMPILER_RT) + + nuttx_add_system_library(rt.buitlins) + + target_include_directories( + rt.buitlins PRIVATE ${INCDIR} + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins) + + target_compile_options(rt.buitlins PRIVATE -Wno-undef -Wno-macro-redefined) + + set(SRCSTMP) + set(RT_BUILTINS_SRCS) + file(GLOB RT_BUILTINS_SRCS + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/*.c) + + file(GLOB SRCSTMP + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/${ARCH}/*.S) + list(APPEND RT_BUILTINS_SRCS ${SRCSTMP}) + + file(GLOB SRCSTMP + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/${ARCH}/*.c) + list(APPEND RT_BUILTINS_SRCS ${SRCSTMP}) + + if(NOT CONFIG_LIB_COMPILER_RT_HAS_BFLOAT16) + set(RT_BUILTINS_BFLOAT16_SRCS + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/truncdfbf2.c + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/truncsfbf2.c) + list(REMOVE_ITEM RT_BUILTINS_SRCS ${RT_BUILTINS_BFLOAT16_SRCS}) endif() - list(APPEND INCDIR ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/include) - - if(CONFIG_BUILTIN_COMPILER_RT) - - nuttx_add_system_library(rt.buitlins) - - target_include_directories( - rt.buitlins PRIVATE ${INCDIR} - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins) - - target_compile_options(rt.buitlins PRIVATE -Wno-undef -Wno-macro-redefined) - - set(SRCSTMP) - set(RT_BUILTINS_SRCS) - file(GLOB RT_BUILTINS_SRCS - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/*.c) - - file(GLOB SRCSTMP - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/${ARCH}/*.S) - list(APPEND RT_BUILTINS_SRCS ${SRCSTMP}) - - file(GLOB SRCSTMP - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/${ARCH}/*.c) - list(APPEND RT_BUILTINS_SRCS ${SRCSTMP}) - - if(NOT CONFIG_LIB_COMPILER_RT_HAS_BFLOAT16) - set(RT_BUILTINS_BFLOAT16_SRCS - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/truncdfbf2.c - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/truncsfbf2.c) - list(REMOVE_ITEM RT_BUILTINS_SRCS ${RT_BUILTINS_BFLOAT16_SRCS}) - endif() - - target_sources(rt.buitlins PRIVATE ${RT_BUILTINS_SRCS}) - - endif() - - if(CONFIG_COVERAGE_COMPILER_RT) - - nuttx_add_system_library(rt.profile) - - target_include_directories( - rt.profile PRIVATE ${INCDIR} - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile) - - target_compile_options( - rt.profile PRIVATE -DCOMPILER_RT_HAS_UNAME -Wno-undef - -Wno-strict-prototypes -Wno-shadow) - - set(SRCSTMP) - set(RT_PROFILE_SRCS InstrProfilingPlatform.c) - - file(GLOB SRCSTMP ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile/*.c) - list(APPEND RT_PROFILE_SRCS ${SRCSTMP}) - - file(GLOB SRCSTMP ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile/*.cpp) - list(APPEND RT_PROFILE_SRCS ${SRCSTMP}) - - target_sources(rt.profile PRIVATE ${RT_PROFILE_SRCS}) - - endif() + target_sources(rt.buitlins PRIVATE ${RT_BUILTINS_SRCS}) endif() + +if(CONFIG_COVERAGE_COMPILER_RT) + + nuttx_add_system_library(rt.profile) + + target_include_directories( + rt.profile PRIVATE ${INCDIR} + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile) + + target_compile_options(rt.profile PRIVATE -DCOMPILER_RT_HAS_UNAME -Wno-undef + -Wno-strict-prototypes -Wno-shadow) + + set(SRCSTMP) + set(RT_PROFILE_SRCS InstrProfilingPlatform.c) + + file(GLOB SRCSTMP ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile/*.c) + list(APPEND RT_PROFILE_SRCS ${SRCSTMP}) + + file(GLOB SRCSTMP ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile/*.cpp) + list(APPEND RT_PROFILE_SRCS ${SRCSTMP}) + + target_sources(rt.profile PRIVATE ${RT_PROFILE_SRCS}) + +elseif(CONFIG_COVERAGE_MINI) + nuttx_add_system_library(rt.miniprofile) + target_sources(rt.profile PRIVATE ${CMAKE_CURRENT_LIST_DIR}/coverage.c) +endif() diff --git a/libs/libbuiltin/compiler-rt/Make.defs b/libs/libbuiltin/compiler-rt/Make.defs index e44fc6ef580..9dd4fd2c6ed 100644 --- a/libs/libbuiltin/compiler-rt/Make.defs +++ b/libs/libbuiltin/compiler-rt/Make.defs @@ -57,8 +57,9 @@ compiler-rt: compiler-rt/compiler-rt endif -.PHONY: compiler-rt +ifeq ($(CONFIG_LIB_COMPILER_RT),y) +.PHONY: compiler-rt depend:: compiler-rt distclean:: @@ -68,6 +69,8 @@ distclean:: FLAGS += ${INCDIR_PREFIX}$(CURDIR)/compiler-rt/compiler-rt/include +endif + ################# Builtin Library ################# ifeq ($(CONFIG_BUILTIN_COMPILER_RT),y) @@ -100,9 +103,17 @@ FLAGS += -DCOMPILER_RT_HAS_UNAME CSRCS += $(wildcard compiler-rt/compiler-rt/lib/profile/*.c) CPPSRCS += $(wildcard compiler-rt/compiler-rt/lib/profile/*.cpp) -CSRCS += compiler-rt/InstrProfilingPlatform.c +CSRCS += InstrProfilingPlatform.c + +else ifeq ($(CONFIG_COVERAGE_MINI),y) + +CSRCS += coverage.c + endif AFLAGS += $(FLAGS) CFLAGS += $(FLAGS) CXXFLAGS += $(FLAGS) + +DEPPATH += --dep-path compiler-rt +VPATH += :compiler-rt diff --git a/libs/libbuiltin/compiler-rt/coverage.c b/libs/libbuiltin/compiler-rt/coverage.c new file mode 100644 index 00000000000..df85b49a0ed --- /dev/null +++ b/libs/libbuiltin/compiler-rt/coverage.c @@ -0,0 +1,326 @@ +/**************************************************************************** + * libs/libbuiltin/compiler-rt/coverage.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define INSTR_PROF_RAW_VERSION 8 +#define INSTR_PROF_RAW_VERSION_VAR __llvm_profile_raw_version +#define INSTR_PROF_PROFILE_RUNTIME_VAR __llvm_profile_runtime + +/* Magic number to detect file format and endianness. + * Use 255 at one end, since no UTF-8 file can use that character. Avoid 0, + * so that utilities, like strings, don't grab it as a string. 129 is also + * invalid UTF-8, and high enough to be interesting. + * Use "lprofr" in the centre to stand for "LLVM Profile Raw", or "lprofR" + * for 32-bit platforms. + */ + +#define INSTR_PROF_RAW_MAGIC_64 \ + (uint64_t)255 << 56 | (uint64_t)'l' << 48 | (uint64_t)'p' << 40 | \ + (uint64_t)'r' << 32 | (uint64_t)'o' << 24 | (uint64_t)'f' << 16 | \ + (uint64_t)'r' << 8 | (uint64_t)129 + +#define INSTR_PROF_RAW_MAGIC_32 \ + (uint64_t)255 << 56 | (uint64_t)'l' << 48 | (uint64_t)'p' << 40 | \ + (uint64_t)'r' << 32 | (uint64_t)'o' << 24 | (uint64_t)'f' << 16 | \ + (uint64_t)'R' << 8 | (uint64_t)129 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum value_kind +{ + IPVK_INDIRECT_CALL_TARGET = 0, + IPVK_MEM_OP_SIZE = 1, + IPVK_FIRST = IPVK_INDIRECT_CALL_TARGET, + IPVK_LAST = IPVK_MEM_OP_SIZE, +}; + +typedef struct aligned_data(8) __llvm_profile_data +{ + const uint64_t name_ref; + const uint64_t func_hash; + const intptr_t counter_ptr; + const intptr_t func_ptr; + intptr_t values; + const uint32_t num_counters; + const uint16_t num_value_sites[IPVK_LAST + 1]; +}__llvm_profile_data; + +typedef struct __llvm_profile_header +{ + uint64_t magic; + uint64_t version; + uint64_t binary_ids_size; + uint64_t data_size; + uint64_t padding_bytes_before_counters; + uint64_t counters_size; + uint64_t padding_bytes_after_counters; + uint64_t names_size; + uint64_t counters_delta; + uint64_t names_delta; + enum value_kind value_kind_last; +}__llvm_profile_header; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static uint64_t INSTR_PROF_RAW_VERSION_VAR = INSTR_PROF_RAW_VERSION; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Record where the data is in memory. Within each of the types of data, + * it's stored consecutively. + */ + +extern char __start__llvm_prf_names[]; +extern char __end__llvm_prf_names[]; +extern char __start__llvm_prf_data[]; +extern char __end__llvm_prf_data[]; +extern char __start__llvm_prf_cnts[]; +extern char __end__llvm_prf_cnts[]; + +int INSTR_PROF_PROFILE_RUNTIME_VAR; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static size_t __llvm_profile_counter_entry_size(void) +{ + return sizeof(uint64_t); +} + +static uint64_t __llvm_profile_get_magic(void) +{ + return sizeof(void *) == sizeof(uint64_t) ? (INSTR_PROF_RAW_MAGIC_64) + : (INSTR_PROF_RAW_MAGIC_32); +} + +static uint64_t __llvm_profile_get_version(void) +{ + return __llvm_profile_raw_version; +} + +static uint64_t __llvm_profile_get_num_counters(const char *begin, + const char *end) +{ + return (((intptr_t)end + __llvm_profile_counter_entry_size() - 1) - + (intptr_t)begin) / __llvm_profile_counter_entry_size(); +} + +static int __llvm_write_binary_ids(void) +{ + return 0; +} + +static const __llvm_profile_data *__llvm_profile_begin_data(void) +{ + return (const __llvm_profile_data *)__start__llvm_prf_data; +} + +static const __llvm_profile_data *__llvm_profile_end_data(void) +{ + return (const __llvm_profile_data *)__end__llvm_prf_data; +} + +static const char *__llvm_profile_begin_names(void) +{ + return (const char *)__start__llvm_prf_names; +} + +static const char *__llvm_profile_end_names(void) +{ + return (const char *)__end__llvm_prf_names; +} + +static char *__llvm_profile_begin_counters(void) +{ + return (char *)__start__llvm_prf_cnts; +} + +static char *__llvm_profile_end_counters(void) +{ + return (char *)__end__llvm_prf_cnts; +} + +static uint64_t __llvm_profile_get_num_padding_bytes(uint64_t size) +{ + return 7 & (sizeof(uint64_t) - size % sizeof(uint64_t)); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +uint64_t __llvm_profile_get_num_data(const __llvm_profile_data *begin, + const __llvm_profile_data *end) +{ + return (((intptr_t)end + sizeof(__llvm_profile_data) - 1) - + (intptr_t)begin) / sizeof(__llvm_profile_data); +} + +/* Given a pointer to the __llvm_profile_data for the function, record the + * bounds of the profile data and profile count sections. + * This function is called several time by the __llvm_profile_init function + * at program start. + * + * If this function is called we register __llvm_profile_dump() with + * atexit to write out the profile information to file. + */ + +void __llvm_profile_register_function(void *data_) +{ + (void)data_; +} + +void __llvm_profile_register_names_function(void *names_start, + uint64_t names_size) +{ + (void)names_start; + (void)names_size; +} + +/* Called by an atexit handler. Writes a file called default.profraw + * containing the profile data. This needs to be merged by + * llvm-prof. See the clang profiling documentation for details. + */ + +void __llvm_profile_dump(const char *path) +{ + int fd; + int ret; + + /* Header: __llvm_profile_header from InstrProfData.inc */ + + const char *filename = path; + + /* Calculate size of sections. */ + + const __llvm_profile_data *data_begin = __llvm_profile_begin_data(); + const __llvm_profile_data *data_end = __llvm_profile_end_data(); + const uint64_t num_data = + __llvm_profile_get_num_data(data_begin, data_end); + + const char *counters_begin = __llvm_profile_begin_counters(); + const char *counters_end = __llvm_profile_end_counters(); + const uint64_t num_counters = + __llvm_profile_get_num_counters(counters_begin, counters_end); + + const char *names_begin = __llvm_profile_begin_names(); + const char *names_end = __llvm_profile_end_names(); + const uint64_t names_size = (names_end - names_begin) * sizeof(char); + + uint64_t padding_bytes_after_names = + __llvm_profile_get_num_padding_bytes(names_size); + + __llvm_profile_header hdr; + hdr.magic = __llvm_profile_get_magic(); + hdr.version = __llvm_profile_get_version(); + hdr.binary_ids_size = __llvm_write_binary_ids(); + hdr.data_size = num_data; + hdr.padding_bytes_before_counters = 0; + hdr.counters_size = num_counters; + hdr.padding_bytes_after_counters = 0; + hdr.names_size = names_size; + hdr.counters_delta = (uintptr_t)counters_begin - (uintptr_t)data_begin; + hdr.names_delta = (uintptr_t)names_begin; + hdr.value_kind_last = IPVK_LAST; + + fd = _NX_OPEN(filename, O_WRONLY | O_CREAT); + if (fd < 0) + { + _NX_SETERRNO(fd); + return; + } + + /* Header */ + + ret = _NX_WRITE(fd, &hdr, sizeof(hdr)); + if (ret != sizeof(hdr)) + { + _NX_SETERRNO(ret); + goto exit; + } + + /* Data */ + + ret = _NX_WRITE(fd, data_begin, sizeof(__llvm_profile_data) * num_data); + if (ret != sizeof(__llvm_profile_data) * num_data) + { + _NX_SETERRNO(ret); + goto exit; + } + + /* Counters */ + + ret = _NX_WRITE(fd, counters_begin, sizeof(uint64_t) * num_counters); + if (ret != sizeof(uint64_t) * num_counters) + { + _NX_SETERRNO(ret); + goto exit; + } + + /* Names */ + + ret = _NX_WRITE(fd, names_begin, names_size); + if (ret != names_size) + { + _NX_SETERRNO(ret); + goto exit; + } + + /* Padding */ + + for (; padding_bytes_after_names != 0; --padding_bytes_after_names) + { + ret = _NX_WRITE(fd, "\0", 1); + if (ret != 1) + { + _NX_SETERRNO(ret); + break; + } + } + +exit: + _NX_CLOSE(fd); +}