This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit b59e3616f4906852a32a12d98caa4c025992fafd Author: wangmingrong1 <[email protected]> AuthorDate: Thu Nov 7 20:02:05 2024 +0800 gcov: Support for the most streamlined profile of LLVM-embedded-toolchain-for-Arm 1. Excerpted from: https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/blob/main/samples/src/cpp-baremetal-semihosting-prof/proflib.c 2. Since llvm profile supports more than just gcov, and some features have not yet been explored, two clang gcov implementations are supported after this patch 3. Using this lib only supports the gcov compilation options of "-fprofile-instr-generate -fcoverage-mapping" 4. This file is heavily dependent on the compiler clang version, and is currently aligned with ci, supporting 17.0.1 and below. 18 and above are not supported by this library due to different internal implementations of the compiler Signed-off-by: wangmingrong1 <[email protected]> --- arch/sim/src/Makefile | 2 +- boards/sim/sim/sim/configs/kasan/defconfig | 2 +- libs/libbuiltin/Kconfig | 29 ++- libs/libbuiltin/Makefile | 2 - libs/libbuiltin/compiler-rt/CMakeLists.txt | 102 ++++----- libs/libbuiltin/compiler-rt/Make.defs | 15 +- libs/libbuiltin/compiler-rt/coverage.c | 326 +++++++++++++++++++++++++++++ 7 files changed, 421 insertions(+), 57 deletions(-) diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 1ebdf70057..08a0f88e42 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 34b43ed9a2..369a239922 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 fa92c8ee8f..e5294112ce 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 de3fb04984..54350fbbcf 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 cb66e68dae..57c33df5c7 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() - - list(APPEND INCDIR ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/include) +endif() - if(CONFIG_BUILTIN_COMPILER_RT) +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() - nuttx_add_system_library(rt.buitlins) +list(APPEND INCDIR ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/include) - target_include_directories( - rt.buitlins PRIVATE ${INCDIR} - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins) +if(CONFIG_BUILTIN_COMPILER_RT) - target_compile_options(rt.buitlins PRIVATE -Wno-undef -Wno-macro-redefined) + nuttx_add_system_library(rt.buitlins) - set(SRCSTMP) - set(RT_BUILTINS_SRCS) - file(GLOB RT_BUILTINS_SRCS - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/*.c) + target_include_directories( + rt.buitlins PRIVATE ${INCDIR} + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins) - file(GLOB SRCSTMP - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/${ARCH}/*.S) - list(APPEND RT_BUILTINS_SRCS ${SRCSTMP}) + target_compile_options(rt.buitlins PRIVATE -Wno-undef -Wno-macro-redefined) - file(GLOB SRCSTMP - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/${ARCH}/*.c) - list(APPEND RT_BUILTINS_SRCS ${SRCSTMP}) + set(SRCSTMP) + set(RT_BUILTINS_SRCS) + file(GLOB RT_BUILTINS_SRCS + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/*.c) - 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() + file(GLOB SRCSTMP + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/${ARCH}/*.S) + list(APPEND RT_BUILTINS_SRCS ${SRCSTMP}) - target_sources(rt.buitlins PRIVATE ${RT_BUILTINS_SRCS}) + 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() - if(CONFIG_COVERAGE_COMPILER_RT) + target_sources(rt.buitlins PRIVATE ${RT_BUILTINS_SRCS}) + +endif() - nuttx_add_system_library(rt.profile) +if(CONFIG_COVERAGE_COMPILER_RT) - target_include_directories( - rt.profile PRIVATE ${INCDIR} - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile) + nuttx_add_system_library(rt.profile) - target_compile_options( - rt.profile PRIVATE -DCOMPILER_RT_HAS_UNAME -Wno-undef - -Wno-strict-prototypes -Wno-shadow) + target_include_directories( + rt.profile PRIVATE ${INCDIR} + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile) - set(SRCSTMP) - set(RT_PROFILE_SRCS InstrProfilingPlatform.c) + target_compile_options(rt.profile PRIVATE -DCOMPILER_RT_HAS_UNAME -Wno-undef + -Wno-strict-prototypes -Wno-shadow) - file(GLOB SRCSTMP ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile/*.c) - list(APPEND RT_PROFILE_SRCS ${SRCSTMP}) + set(SRCSTMP) + set(RT_PROFILE_SRCS InstrProfilingPlatform.c) - file(GLOB SRCSTMP ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile/*.cpp) - list(APPEND RT_PROFILE_SRCS ${SRCSTMP}) + file(GLOB SRCSTMP ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile/*.c) + list(APPEND RT_PROFILE_SRCS ${SRCSTMP}) - target_sources(rt.profile PRIVATE ${RT_PROFILE_SRCS}) + file(GLOB SRCSTMP ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/profile/*.cpp) + list(APPEND RT_PROFILE_SRCS ${SRCSTMP}) - endif() + 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 e44fc6ef58..9dd4fd2c6e 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 0000000000..df85b49a0e --- /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 <nuttx/compiler.h> + +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <fcntl.h> +#include <nuttx/fs/fs.h> + +/**************************************************************************** + * 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); +}
