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 <wangmingrong1@xiaomi.com>
This commit is contained in:
wangmingrong1 2024-11-07 20:02:05 +08:00 committed by Xiang Xiao
parent cd2633ddcc
commit b59e3616f4
7 changed files with 439 additions and 75 deletions

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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()

View file

@ -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

View file

@ -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);
}