From 65970f029b88f100aeb41e95382697aeeb41d570 Mon Sep 17 00:00:00 2001 From: yinshengkai Date: Mon, 18 Nov 2024 22:21:53 +0800 Subject: [PATCH 01/11] libs: add gcov framework support In devices without storage media, you can export data to the command line and then generate the corresponding gcda file It can save the result output by calling __gcov_info_to_gcda The usage is similar to: https://gcc.gnu.org/onlinedocs/gcc/Freestanding-Environments.html#Profiling-and-Test-Coverage-in-Freestanding-Environments Usage: ./tools/configure.sh qemu-armv7a:nsh Modify the configuration +CONFIG_COVERAGE_ALL=y +CONFIG_COVERAGE_MINI=y +CONFIG_SYSTEM_GCOV=y Run: qemu-system-arm -cpu cortex-a7 -nographic -smp 4 \ -machine virt,virtualization=off,gic-version=2 \ -net none -chardev stdio,id=con,mux=on -serial chardev:con \ -mon chardev=con,mode=readline -kernel ./nuttx/nuttx -semihosting -s | tee gcov.txt ./nuttx/tools/gcov_convert.py -i ./gcov.txt ./nuttx/tools/gcov.sh -t arm-none-eabi-gcov Signed-off-by: yinshengkai --- include/gcov.h | 169 ++++++++ libs/libbuiltin/compiler-rt/CMakeLists.txt | 102 ++--- libs/libbuiltin/compiler-rt/Make.defs | 2 +- libs/libbuiltin/libgcc/CMakeLists.txt | 9 +- libs/libbuiltin/libgcc/Make.defs | 8 +- libs/libbuiltin/libgcc/gcov.c | 431 +++++++++++++++++++++ tools/gcov_convert.py | 94 +++++ 7 files changed, 748 insertions(+), 67 deletions(-) create mode 100644 include/gcov.h create mode 100644 libs/libbuiltin/libgcc/gcov.c create mode 100755 tools/gcov_convert.py diff --git a/include/gcov.h b/include/gcov.h new file mode 100644 index 00000000000..e494fe1e50d --- /dev/null +++ b/include/gcov.h @@ -0,0 +1,169 @@ +/**************************************************************************** + * include/gcov.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_GCOV_H +#define __INCLUDE_GCOV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The GCOV 12 gcno/gcda format has slight change, + * Please refer to gcov-io.h in the GCC 12 for + * more details. + */ + +#if __GNUC__ >= 12 +# define GCOV_12_FORMAT +#endif + +#if __GNUC__ >= 14 +# define GCOV_COUNTERS 9u +#elif __GNUC__ >= 10 +# define GCOV_COUNTERS 8u +#elif __GNUC__ >= 8 +# define GCOV_COUNTERS 9u +#else +# define GCOV_COUNTERS 10u +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct gcov_fn_info; +typedef uint64_t gcov_type; + +/** Profiling data per object file + * + * This data is generated by gcc during compilation and doesn't change + * at run-time with the exception of the next pointer. + */ + +struct gcov_info +{ + unsigned int version; /* Gcov version (same as GCC version) */ + FAR struct gcov_info *next; /* List head for a singly-linked list */ + unsigned int stamp; /* Uniquifying time stamp */ +#ifdef GCOV_12_FORMAT + unsigned int checksum; /* unique object checksum */ +#endif + FAR const char *filename; /* Name of the associated gcda data file */ + void (*merge[GCOV_COUNTERS])(FAR gcov_type *, unsigned int); + unsigned int n_functions; /* number of instrumented functions */ + FAR struct gcov_fn_info **functions; /* function information */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern FAR struct gcov_info *__gcov_info_start; +extern FAR struct gcov_info *__gcov_info_end; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: __gcov_reset + * + * Description: + * Set all counters to zero. + * + ****************************************************************************/ + +extern void __gcov_reset(void); + +/**************************************************************************** + * Name: __gcov_dump + * + * Description: + * Write profile information to a file. + * + ****************************************************************************/ + +extern void __gcov_dump(void); + +/**************************************************************************** + * Name: __gcov_info_to_gcda + * + * Description: + * Convert the gcov information referenced by INFO to a gcda data stream. + * + * Parameters: + * info - Pointer to the gcov information. + * filename - Callback function to get the filename. + * dump - Callback function to write the gcda data. + * allocate - Callback function to allocate memory. + * arg - User-provided argument. + * + ****************************************************************************/ + +extern void __gcov_info_to_gcda(FAR const struct gcov_info *info, + FAR void (*filename)(FAR const char *, + FAR void *), + FAR void (*dump)(FAR const void *, + unsigned int, FAR void *), + FAR void *(*allocate)(unsigned int, + FAR void *), + FAR void *arg); + +/**************************************************************************** + * Name: __gcov_filename_to_gcfn + * + * Description: + * Convert the filename to a gcfn data stream. + * + * Parameters: + * filename - Pointer to the filename. + * dump - Callback function to write the gcfn data. + * arg - User-provided argument. + * + ****************************************************************************/ + +extern void __gcov_filename_to_gcfn(FAR const char *filename, + FAR void (*dump)(FAR const void *, + unsigned int, + FAR void *), + FAR void *arg); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_GCOV_H */ diff --git a/libs/libbuiltin/compiler-rt/CMakeLists.txt b/libs/libbuiltin/compiler-rt/CMakeLists.txt index 96731c6c82b..ab6c36763a2 100644 --- a/libs/libbuiltin/compiler-rt/CMakeLists.txt +++ b/libs/libbuiltin/compiler-rt/CMakeLists.txt @@ -52,77 +52,57 @@ 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) - - 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) +endif() - target_compile_options(rt.buitlins PRIVATE -Wno-undef -Wno-macro-redefined) +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() - set(SRCSTMP) - set(RT_BUILTINS_SRCS) - file(GLOB RT_BUILTINS_SRCS - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/*.c) +list(APPEND INCDIR ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/include) - file(GLOB SRCSTMP - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/${ARCH}/*.S) - list(APPEND RT_BUILTINS_SRCS ${SRCSTMP}) +if(CONFIG_BUILTIN_COMPILER_RT) - file(GLOB SRCSTMP - ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins/${ARCH}/*.c) - list(APPEND RT_BUILTINS_SRCS ${SRCSTMP}) + nuttx_add_system_library(rt.buitlins) - 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_include_directories( + rt.buitlins PRIVATE ${INCDIR} + ${CMAKE_CURRENT_LIST_DIR}/compiler-rt/lib/builtins) - target_sources(rt.buitlins PRIVATE ${RT_BUILTINS_SRCS}) + 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() - 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}) - + if(NOT CONFIG_COVERAGE_NONE) + target_compile_options(rt.buitlins -fno-profile-instr-generate + -fno-coverage-mapping) endif() + target_sources(rt.buitlins PRIVATE ${RT_BUILTINS_SRCS}) + endif() if(CONFIG_COVERAGE_COMPILER_RT) @@ -150,7 +130,7 @@ if(CONFIG_COVERAGE_COMPILER_RT) target_sources(rt.profile PRIVATE ${RT_PROFILE_SRCS}) -elseif(CONFIG_COVERAGE_MINI) +elseif(CONFIG_COVERAGE_MINI AND CONFIG_ARCH_TOOLCHAIN_CLANG) nuttx_add_system_library(rt.miniprofile) target_compile_options(rt.miniprofile -fno-profile-instr-generate diff --git a/libs/libbuiltin/compiler-rt/Make.defs b/libs/libbuiltin/compiler-rt/Make.defs index 93faaf892b1..c9fedfe74f6 100644 --- a/libs/libbuiltin/compiler-rt/Make.defs +++ b/libs/libbuiltin/compiler-rt/Make.defs @@ -106,7 +106,7 @@ CSRCS += $(wildcard compiler-rt/compiler-rt/lib/profile/*.c) CPPSRCS += $(wildcard compiler-rt/compiler-rt/lib/profile/*.cpp) CSRCS += InstrProfilingPlatform.c -else ifeq ($(CONFIG_COVERAGE_MINI),y) +else ifeq ($(CONFIG_COVERAGE_MINI)$(CONFIG_ARCH_TOOLCHAIN_CLANG),yy) FLAGS += -fno-profile-instr-generate -fno-coverage-mapping diff --git a/libs/libbuiltin/libgcc/CMakeLists.txt b/libs/libbuiltin/libgcc/CMakeLists.txt index ad5624c4f4b..b944bc183dd 100644 --- a/libs/libbuiltin/libgcc/CMakeLists.txt +++ b/libs/libbuiltin/libgcc/CMakeLists.txt @@ -19,6 +19,11 @@ # ############################################################################## if(CONFIG_PROFILE_MINI) - nuttx_add_system_library(libgprof) - target_sources(libgprof PRIVATE profile.c) + nuttx_add_system_library(libprofile) + target_sources(libprofile PRIVATE profile.c) +endif() + +if(CONFIG_COVERAGE_MINI AND CONFIG_ARCH_TOOLCHAIN_GCC) + nuttx_add_system_library(libcoverage) + target_sources(libcoverage PRIVATE gcov.c) endif() diff --git a/libs/libbuiltin/libgcc/Make.defs b/libs/libbuiltin/libgcc/Make.defs index 6d1a9260831..ec8a2101a91 100644 --- a/libs/libbuiltin/libgcc/Make.defs +++ b/libs/libbuiltin/libgcc/Make.defs @@ -19,10 +19,12 @@ ############################################################################ ifeq ($(CONFIG_PROFILE_MINI),y) - CSRCS += profile.c +endif + +ifeq ($(CONFIG_COVERAGE_MINI)$(CONFIG_ARCH_TOOLCHAIN_GCC),yy) +CSRCS += gcov.c +endif DEPPATH += --dep-path libgcc VPATH += :libgcc - -endif diff --git a/libs/libbuiltin/libgcc/gcov.c b/libs/libbuiltin/libgcc/gcov.c new file mode 100644 index 00000000000..a98ae36cd86 --- /dev/null +++ b/libs/libbuiltin/libgcc/gcov.c @@ -0,0 +1,431 @@ +/**************************************************************************** + * libs/libbuiltin/libgcc/gcov.c + * + * 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 + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GCOV_DATA_MAGIC (0x67636461) +#define GCOV_NOTE_MAGIC (0x67636e6f) +#define GCOV_FILENAME_MAGIC (0x6763666e) + +#define GCOV_TAG_FUNCTION (0x01000000) +#define GCOV_TAG_COUNTER_BASE (0x01a10000) + +#define GCOV_TAG_FOR_COUNTER(count) \ + (GCOV_TAG_COUNTER_BASE + ((uint32_t)(count) << 17)) + +#ifdef GCOV_12_FORMAT +# define GCOV_TAG_FUNCTION_LENGTH 12 +#else +# define GCOV_TAG_FUNCTION_LENGTH 3 +#endif + +#ifdef GCOV_12_FORMAT +# define GCOV_UNIT_SIZE 4 +#else +# define GCOV_UNIT_SIZE 1 +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef unsigned int gcov_unsigned_t; + +/* Information about counters for a single function + * + * This data is generated by gcc during compilation and doesn't change + * at run-time. + */ + +struct gcov_ctr_info +{ + unsigned int num; /* Number of counter values for this type */ + FAR gcov_type *values; /* Array of counter values for this type */ +}; + +/* Profiling meta data per function + * + * This data is generated by gcc during compilation and doesn't change + * at run-time. + */ + +struct gcov_fn_info +{ + FAR const struct gcov_info *key; /* Comdat key */ + unsigned int ident; /* Unique ident of function */ + unsigned int lineno_checksum; /* Function lineno checksum */ + unsigned int cfg_checksum; /* Function cfg checksum */ + struct gcov_ctr_info ctrs[1]; /* Instrumented counters */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +FAR struct gcov_info *__gcov_info_start; +FAR struct gcov_info *__gcov_info_end; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void dump_counter(FAR void *buffer, FAR size_t *off, uint64_t v) +{ + if (buffer) + { + *(FAR uint64_t *)((FAR uint8_t *)buffer + *off) = v; + } + + *off += sizeof(uint64_t); +} + +static void dump_unsigned(FAR void *buffer, FAR size_t *off, uint32_t v) +{ + if (buffer) + { + *(FAR uint32_t *)((FAR uint8_t *)buffer + *off) = v; + } + + *off += sizeof(uint32_t); +} + +static size_t gcov_convert(FAR uint8_t *buffer, + FAR const struct gcov_info *info) +{ + FAR struct gcov_fn_info *gfi; + FAR struct gcov_ctr_info *gci; + uint32_t functions; + uint32_t counts; + uint32_t value; + size_t pos = 0; + + /* File header. */ + + dump_unsigned(buffer, &pos, GCOV_DATA_MAGIC); + dump_unsigned(buffer, &pos, info->version); + dump_unsigned(buffer, &pos, info->stamp); + +#ifdef GCOV_12_FORMAT + dump_unsigned(buffer, &pos, info->checksum); +#endif + + /* Function headers. */ + + for (functions = 0; functions < info->n_functions; functions++) + { + gfi = info->functions[functions]; + + /* Function record. */ + + dump_unsigned(buffer, &pos, GCOV_TAG_FUNCTION); + dump_unsigned(buffer, &pos, GCOV_TAG_FUNCTION_LENGTH); + dump_unsigned(buffer, &pos, gfi->ident); + dump_unsigned(buffer, &pos, gfi->lineno_checksum); + dump_unsigned(buffer, &pos, gfi->cfg_checksum); + + gci = gfi->ctrs; + for (counts = 0; counts < GCOV_COUNTERS; counts++) + { + if (!info->merge[counts]) + { + continue; + } + + /* Counter record. */ + + dump_unsigned(buffer, &pos, GCOV_TAG_FOR_COUNTER(counts)); + dump_unsigned(buffer, &pos, gci->num * 2 * GCOV_UNIT_SIZE); + + for (value = 0; value < gci->num; value++) + { + dump_counter(buffer, &pos, gci->values[value]); + } + + gci++; + } + } + + return pos; +} + +static int gcov_process_path(FAR char *prefix, int strip, + FAR char *path, FAR char *new_path, + size_t len) +{ + FAR char *tokens[64]; + FAR char *filename; + FAR char *token; + int token_count = 0; + int prefix_count; + int level = 0; + int ret; + int i; + + token = strtok(prefix, "/"); + while (token != NULL) + { + tokens[token_count++] = token; + token = strtok(NULL, "/"); + } + + /* Split the path into directories and filename */ + + prefix_count = token_count; + token = strtok(path, "/"); + while (token != NULL) + { + filename = token; + if (level++ >= strip) + { + /* Skip the specified number of leading directories */ + + if (token_count >= sizeof(tokens) / sizeof(tokens[0])) + { + return -ENAMETOOLONG; + } + + tokens[token_count++] = token; + } + + token = strtok(NULL, "/"); + } + + /* Add the filename */ + + if (prefix_count == token_count) + { + tokens[token_count++] = filename; + } + + new_path[0] = '\0'; + tokens[token_count] = NULL; + + /* Check and create directories */ + + for (i = 0; i < token_count - 1; i++) + { + strcat(new_path, "/"); + strcat(new_path, tokens[i]); + if (access(new_path, F_OK) != 0) + { + ret = mkdir(new_path, 0644); + if (ret != 0) + { + return -errno; + } + } + } + + strcat(new_path, "/"); + strcat(new_path, filename); + return 0; +} + +static int gcov_write_file(FAR const char *filename, + FAR const struct gcov_info *info) +{ + FAR uint8_t *buffer; + size_t written; + int ret = OK; + size_t size; + int fd; + + fd = _NX_OPEN(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + syslog(LOG_ERR, "open %s failed!", filename); + return -errno; + } + + size = gcov_convert(NULL, info); + buffer = lib_malloc(size); + if (buffer == NULL) + { + syslog(LOG_ERR, "gcov alloc failed!"); + _NX_CLOSE(fd); + return -errno; + } + + gcov_convert(buffer, info); + written = _NX_WRITE(fd, buffer, size); + if (written != size) + { + syslog(LOG_ERR, "gcov write file failed!"); + ret = -errno; + } + + _NX_CLOSE(fd); + lib_free(buffer); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void __gcov_init(FAR struct gcov_info *info) +{ + info->next = __gcov_info_start; + __gcov_info_start = info; +} + +void __gcov_merge_add(FAR gcov_type *counters, unsigned int n_counters) +{ +} + +void __gcov_exit(void) +{ +} + +void __gcov_dump(void) +{ + FAR struct gcov_info *info; + FAR const char *strip = getenv("GCOV_PREFIX_STRIP"); + FAR const char *prefix = getenv("GCOV_PREFIX"); + FAR char *prefix2 = strdup(prefix); + FAR char new_path[PATH_MAX]; + int ret; + + for (info = __gcov_info_start; info; info = info->next) + { + FAR char *filename; + + filename = strdup(info->filename); + if (filename == NULL) + { + syslog(LOG_ERR, "gcov alloc failed! skip %s", info->filename); + continue; + } + + /* Process the path, add the prefix and strip the leading directories */ + + strcpy(prefix2, prefix); + ret = gcov_process_path(prefix2, atoi(strip), filename, + new_path, PATH_MAX); + if (ret != 0) + { + syslog(LOG_ERR, "gcov process path failed! skip %s", + new_path); + lib_free(filename); + continue; + } + + /* Convert the data and write to the file */ + + ret = gcov_write_file(new_path, info); + if (ret != 0) + { + syslog(LOG_ERR, "gcov write file failed! skip %s", new_path); + lib_free(filename); + continue; + } + + lib_free(filename); + } + + lib_free(prefix2); +} + +void __gcov_reset(void) +{ + FAR struct gcov_info *info; + FAR struct gcov_ctr_info *gci; + uint32_t functions; + uint32_t counts; + + for (info = __gcov_info_start; info; info = info->next) + { + for (functions = 0; functions < info->n_functions; functions++) + { + gci = info->functions[functions]->ctrs; + + for (counts = 0; counts < GCOV_COUNTERS; counts++) + { + if (!info->merge[counts]) + { + continue; + } + + memset(gci->values, 0, gci->num * sizeof(gcov_type)); + gci++; + } + } + } +} + +void __gcov_filename_to_gcfn(FAR const char *filename, + FAR void (*dump_fn)(FAR const void *, + unsigned, FAR void *), + FAR void *arg) +{ + if (dump_fn) + { + size_t len = strlen(filename); + dump_fn(filename, len, arg); + } +} + +void __gcov_info_to_gcda(FAR const struct gcov_info *info, + FAR void (*filename_fn)(FAR const char *, + FAR void *), + FAR void (*dump_fn)(FAR const void *, unsigned int, + FAR void *), + FAR void *(*allocate_fn)(unsigned int, FAR void *), + FAR void *arg) +{ + FAR const char *name = info->filename; + FAR uint8_t *buffer; + size_t size; + + filename_fn(name, arg); + + /* Get the size of the buffer */ + + size = gcov_convert(NULL, info); + + buffer = lib_malloc(size); + if (!buffer) + { + syslog(LOG_ERR, "gcov alloc failed!"); + return; + } + + /* Convert the data */ + + gcov_convert(buffer, info); + dump_fn(buffer, size, arg); + lib_free(buffer); +} diff --git a/tools/gcov_convert.py b/tools/gcov_convert.py new file mode 100755 index 00000000000..7648784d853 --- /dev/null +++ b/tools/gcov_convert.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +############################################################################ +# tools/gcov_convert.py +# +# 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. +# +############################################################################ + +import argparse +import re + +# Parse gcda data from stdout dump format to binary format +# The stdout dump format is: +# gcov start filename: size: Byte +# +# gcov end filename: checksum: +# +# The hex dump data will be converted to binary and written to +# The checksum is calculated by summing all bytes and taking modulo 65536 + + +def parse_gcda_data(input_file): + with open(input_file, "r") as file: + lines = file.read().strip().splitlines() + + for line in lines: + if not line.startswith("gcov start"): + continue + + match = re.search(r"filename:(.*?)\s+size:\s*(\d+)Byte", line) + if not match: + continue + + hex_dump = "" + filename = match.group(1) + size = int(match.group(2)) + + # Read the hex dump until the end of the file + next_line_index = lines.index(line) + 1 + while next_line_index < len(lines) and not lines[next_line_index].startswith( + "gcov end" + ): + hex_dump += lines[next_line_index].strip() + next_line_index += 1 + + if size != len(hex_dump) // 2: + print( + f"Size mismatch for {filename}: expected {size} bytes, got {len(hex_dump) // 2} bytes" + ) + + checksum_match = ( + re.search(r"checksum:\s*(0x[0-9a-fA-F]+)", lines[next_line_index]) + if next_line_index < len(lines) + else None + ) + if not checksum_match: + continue + + checksum = int(checksum_match.group(1), 16) + calculated_checksum = sum(bytearray.fromhex(hex_dump)) % 65536 + if calculated_checksum != checksum: + print( + f"Checksum mismatch for {filename}: expected {checksum}, got {calculated_checksum}" + ) + continue + + with open(filename, "wb") as fp: + fp.write(bytes.fromhex(hex_dump)) + print(f"write {filename} success") + + print( + "Execute the 'nuttx/tools/gcov.sh -t arm-none-eabi-gcov' command to view the coverage report" + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-i", "--input", required=True, help="Input dump data") + args = parser.parse_args() + + parse_gcda_data(args.input) From f450daf41912f98c7ea5ff8b3064907f6a5458ec Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Thu, 21 Nov 2024 17:27:35 +0800 Subject: [PATCH 02/11] Clang/gcov: Supports gcc standard output interface "__gcov_dump" Signed-off-by: wangmingrong1 --- libs/libbuiltin/compiler-rt/coverage.c | 97 +++++++++++++------------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/libs/libbuiltin/compiler-rt/coverage.c b/libs/libbuiltin/compiler-rt/coverage.c index df85b49a0ed..69520f27515 100644 --- a/libs/libbuiltin/compiler-rt/coverage.c +++ b/libs/libbuiltin/compiler-rt/coverage.c @@ -33,13 +33,13 @@ #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. @@ -98,12 +98,6 @@ typedef struct __llvm_profile_header 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 ****************************************************************************/ @@ -138,7 +132,7 @@ static uint64_t __llvm_profile_get_magic(void) static uint64_t __llvm_profile_get_version(void) { - return __llvm_profile_raw_version; + return INSTR_PROF_RAW_VERSION; } static uint64_t __llvm_profile_get_num_counters(const char *begin, @@ -225,14 +219,10 @@ void __llvm_profile_register_names_function(void *names_start, * llvm-prof. See the clang profiling documentation for details. */ -void __llvm_profile_dump(const char *path) +size_t __llvm_profile_dump(FAR struct lib_outstream_s *stream) { - int fd; - int ret; - - /* Header: __llvm_profile_header from InstrProfData.inc */ - - const char *filename = path; + const char c = '\0'; + size_t size = 0; /* Calculate size of sections. */ @@ -266,61 +256,74 @@ void __llvm_profile_dump(const char *path) 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)) + size += sizeof(hdr); + if (sizeof(hdr) != stream->puts(stream, &hdr, 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) + size += sizeof(__llvm_profile_data) * num_data; + if (sizeof(__llvm_profile_data) * num_data != + stream->puts(stream, data_begin, + 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) + size += sizeof(uint64_t) * num_counters; + if (sizeof(uint64_t) * num_counters != + stream->puts(stream, counters_begin, + sizeof(uint64_t) * num_counters)) { - _NX_SETERRNO(ret); goto exit; } - /* Names */ - - ret = _NX_WRITE(fd, names_begin, names_size); - if (ret != names_size) + size += names_size; + if (names_size != stream->puts(stream, names_begin, 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) + size += 1; + if (1 != stream->puts(stream, &c, 1)) { - _NX_SETERRNO(ret); break; } } exit: + + return size; +} + +void __gcov_dump(void) +{ + struct lib_rawoutstream_s stream; + FAR char *path; + int fd; + + path = getenv("GCOV_PREFIX"); + if (!path) + { + return; + } + + fd = _NX_OPEN(path, O_WRONLY | O_CREAT); + if (fd < 0) + { + _NX_SETERRNO(fd); + return; + } + + lib_rawoutstream(&stream, fd); + + __llvm_profile_dump(&stream.common); + _NX_CLOSE(fd); } + +void __gcov_reset(void) +{ +} From c0c0038b2bbb60c8680bfd73d7ac6ec409fafdcf Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Fri, 22 Nov 2024 09:51:58 +0800 Subject: [PATCH 03/11] clang/gcov: Add a way to directly dump memory address Signed-off-by: wangmingrong1 --- include/gcov.h | 14 ++++++++++++++ libs/libbuiltin/compiler-rt/coverage.c | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/include/gcov.h b/include/gcov.h index e494fe1e50d..1faad7a9a87 100644 --- a/include/gcov.h +++ b/include/gcov.h @@ -161,6 +161,20 @@ extern void __gcov_filename_to_gcfn(FAR const char *filename, FAR void *), FAR void *arg); +/**************************************************************************** + * Name: __gcov_dump_to_memory + * + * Description: + * Dump gcov data directly to memory + * + * Parameters: + * ptr - Memory Address + * size - Memory block size + * + ****************************************************************************/ + +size_t __gcov_dump_to_memory(FAR void *ptr, size_t size); + #undef EXTERN #ifdef __cplusplus } diff --git a/libs/libbuiltin/compiler-rt/coverage.c b/libs/libbuiltin/compiler-rt/coverage.c index 69520f27515..ab24d5f7b2f 100644 --- a/libs/libbuiltin/compiler-rt/coverage.c +++ b/libs/libbuiltin/compiler-rt/coverage.c @@ -324,6 +324,15 @@ void __gcov_dump(void) _NX_CLOSE(fd); } +size_t __gcov_dump_to_memory(FAR void *ptr, size_t size) +{ + struct lib_memoutstream_s stream; + + lib_memoutstream(&stream, ptr, size); + + return __llvm_profile_dump(&stream.common); +} + void __gcov_reset(void) { } From ed1e58763941e318ae9d67162288671e2dc1eb8f Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Tue, 26 Nov 2024 11:12:07 +0800 Subject: [PATCH 04/11] Fix compilation errors CC: gcov.c gcov.c: In function 'gcov_stdout_dump': gcov.c:146:50: error: passing argument 3 of '__gcov_info_to_gcda' from incompatible pointer type [-Werror=incompatible-pointer-types] 146 | __gcov_info_to_gcda(info, stdout_filename, stdout_dump, NULL, &arg); | ^~~~~~~~~~~ | | | void (*)(const void *, size_t, void *) {aka void (*)(const void *, long unsigned int, void *)} In file included from gcov.c:25: /mnt/vela/github/NX/nuttx/include/gcov.h:139:44: note: expected 'void (*)(const void *, unsigned int, void *)' but argument is of type 'void (*)(const void *, size_t, void *)' {aka 'void (*)(const void *, long unsigned int, void *)'} 139 | FAR void (*dump)(FAR const void *, | ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~ 140 | unsigned int, FAR void *), | ~~~~~~~~~~~~~~~~~~~~~~~~~ libgcc/gcov.c: In function 'gcov_process_path.constprop': libgcc/gcov.c:235:29: error: 'filename' may be used uninitialized [-Werror=maybe-uninitialized] 235 | tokens[token_count++] = filename; | ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~ libgcc/gcov.c:189:13: note: 'filename' was declared here 189 | FAR char *filename; Signed-off-by: wangmingrong1 --- include/gcov.h | 2 +- libs/libbuiltin/libgcc/gcov.c | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/gcov.h b/include/gcov.h index 1faad7a9a87..3c6251b82ce 100644 --- a/include/gcov.h +++ b/include/gcov.h @@ -137,7 +137,7 @@ extern void __gcov_info_to_gcda(FAR const struct gcov_info *info, FAR void (*filename)(FAR const char *, FAR void *), FAR void (*dump)(FAR const void *, - unsigned int, FAR void *), + size_t, FAR void *), FAR void *(*allocate)(unsigned int, FAR void *), FAR void *arg); diff --git a/libs/libbuiltin/libgcc/gcov.c b/libs/libbuiltin/libgcc/gcov.c index a98ae36cd86..eac1c3c5394 100644 --- a/libs/libbuiltin/libgcc/gcov.c +++ b/libs/libbuiltin/libgcc/gcov.c @@ -205,6 +205,11 @@ static int gcov_process_path(FAR char *prefix, int strip, prefix_count = token_count; token = strtok(path, "/"); + if (token == NULL) + { + return -EINVAL; + } + while (token != NULL) { filename = token; @@ -315,10 +320,23 @@ void __gcov_dump(void) FAR struct gcov_info *info; FAR const char *strip = getenv("GCOV_PREFIX_STRIP"); FAR const char *prefix = getenv("GCOV_PREFIX"); - FAR char *prefix2 = strdup(prefix); FAR char new_path[PATH_MAX]; + FAR char *prefix2; int ret; + if (prefix == NULL) + { + syslog(LOG_ERR, "No path prefix specified"); + return; + } + + prefix2 = strdup(prefix); + if (prefix2 == NULL) + { + syslog(LOG_ERR, "gcov alloc failed!"); + return; + } + for (info = __gcov_info_start; info; info = info->next) { FAR char *filename; @@ -401,7 +419,7 @@ void __gcov_filename_to_gcfn(FAR const char *filename, void __gcov_info_to_gcda(FAR const struct gcov_info *info, FAR void (*filename_fn)(FAR const char *, FAR void *), - FAR void (*dump_fn)(FAR const void *, unsigned int, + FAR void (*dump_fn)(FAR const void *, size_t, FAR void *), FAR void *(*allocate_fn)(unsigned int, FAR void *), FAR void *arg) From d17a1c510675f19f298117e32a5b376aea180c7f Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Tue, 3 Dec 2024 18:25:51 +0800 Subject: [PATCH 05/11] gcc/gcov: Add instrumentation function Some functions will insert these functions: void __gcov_execve(void) { } void __gcov_execv(void) { } void __gcov_fork(void) { } Signed-off-by: wangmingrong1 --- libs/libbuiltin/libgcc/gcov.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libs/libbuiltin/libgcc/gcov.c b/libs/libbuiltin/libgcc/gcov.c index eac1c3c5394..987efa1b603 100644 --- a/libs/libbuiltin/libgcc/gcov.c +++ b/libs/libbuiltin/libgcc/gcov.c @@ -315,6 +315,18 @@ void __gcov_exit(void) { } +void __gcov_execve(void) +{ +} + +void __gcov_execv(void) +{ +} + +void __gcov_fork(void) +{ +} + void __gcov_dump(void) { FAR struct gcov_info *info; From 46cb5f35723e545a074bdeea847716f975ac4630 Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Mon, 9 Dec 2024 11:39:55 +0800 Subject: [PATCH 06/11] gcov.c: Add necessary instrumentation functions (.text.ltp_interfaces_sem_unlink_2_2_main+0xd8): undefined reference to `__gcov_execlk' Signed-off-by: wangmingrong1 --- libs/libbuiltin/libgcc/gcov.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/libbuiltin/libgcc/gcov.c b/libs/libbuiltin/libgcc/gcov.c index 987efa1b603..7ec20727286 100644 --- a/libs/libbuiltin/libgcc/gcov.c +++ b/libs/libbuiltin/libgcc/gcov.c @@ -319,6 +319,10 @@ void __gcov_execve(void) { } +void __gcov_execl(void) +{ +} + void __gcov_execv(void) { } From fb7d563c7639010b9c6318db20aa3130c99fc411 Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Thu, 12 Dec 2024 16:47:20 +0800 Subject: [PATCH 07/11] gcov: Fix gcov fork() issue After code coverage is enabled, fork will be replaced by __gcov_fork Signed-off-by: wangmingrong1 --- libs/libbuiltin/libgcc/gcov.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/libbuiltin/libgcc/gcov.c b/libs/libbuiltin/libgcc/gcov.c index 7ec20727286..914fbdc50db 100644 --- a/libs/libbuiltin/libgcc/gcov.c +++ b/libs/libbuiltin/libgcc/gcov.c @@ -327,8 +327,9 @@ void __gcov_execv(void) { } -void __gcov_fork(void) +pid_t __gcov_fork(void) { + return fork(); } void __gcov_dump(void) From c3f2e1fdb7c187bd9b4a08b650f7a97f87950b10 Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Fri, 13 Dec 2024 11:29:21 +0800 Subject: [PATCH 08/11] gcov: Prevent pile insertion recursion Signed-off-by: wangmingrong1 --- libs/libbuiltin/libgcc/CMakeLists.txt | 2 ++ libs/libbuiltin/libgcc/Make.defs | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/libs/libbuiltin/libgcc/CMakeLists.txt b/libs/libbuiltin/libgcc/CMakeLists.txt index b944bc183dd..00fa268cedc 100644 --- a/libs/libbuiltin/libgcc/CMakeLists.txt +++ b/libs/libbuiltin/libgcc/CMakeLists.txt @@ -25,5 +25,7 @@ endif() if(CONFIG_COVERAGE_MINI AND CONFIG_ARCH_TOOLCHAIN_GCC) nuttx_add_system_library(libcoverage) + target_compile_options(libcoverage PRIVATE -fno-profile-arcs + -fno-test-coverage) target_sources(libcoverage PRIVATE gcov.c) endif() diff --git a/libs/libbuiltin/libgcc/Make.defs b/libs/libbuiltin/libgcc/Make.defs index ec8a2101a91..0cdb47efa16 100644 --- a/libs/libbuiltin/libgcc/Make.defs +++ b/libs/libbuiltin/libgcc/Make.defs @@ -24,6 +24,13 @@ endif ifeq ($(CONFIG_COVERAGE_MINI)$(CONFIG_ARCH_TOOLCHAIN_GCC),yy) CSRCS += gcov.c + +GCOV_CFLAGS += -fno-profile-arcs -fno-test-coverage +GCOV_CFLAGS += -fno-stack-protector + +bin/gcov.o: CFLAGS += $(GCOV_CFLAGS) +kbin/gcov.o: CFLAGS += $(GCOV_CFLAGS) + endif DEPPATH += --dep-path libgcc From 444b7c851b7934310c417c2faccc96a1c4279b7a Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Fri, 13 Dec 2024 15:35:29 +0800 Subject: [PATCH 09/11] gcov: Disable stack checking When enable CONFIG_STACK_CANARIES, in general, the stack check in the __gcov_fork function is: " return fork(); 18: e59f3020 ldr r3, [pc, #32] @ 40 <__gcov_fork+0x40> 1c: e5932000 ldr r2, [r3] 20: e59d3004 ldr r3, [sp, #4] 24: e0332002 eors r2, r3, r2 28: e3a03000 mov r3, #0 2c: 1a000002 bne 3c <__gcov_fork+0x3c>" r3 is obtained by taking the value of sp offset. But after opening thumb, the second comparison value in "8c6: 4a06 ldr r2, [pc, #24] @ (8e0 <__gcov_fork+0x30>) 8c8: 6811 ldr r1, [r2, #0] 8ca: 687a ldr r2, [r7, #4] 8cc: 4051 eors r1, r2" is obtained through r7. Since r7 stores the stack address at this time, which stores the address of the parent process, the stack out of bounds will occur in the child process Signed-off-by: wangmingrong1 --- libs/libbuiltin/libgcc/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/libbuiltin/libgcc/CMakeLists.txt b/libs/libbuiltin/libgcc/CMakeLists.txt index 00fa268cedc..52f3faa7ba8 100644 --- a/libs/libbuiltin/libgcc/CMakeLists.txt +++ b/libs/libbuiltin/libgcc/CMakeLists.txt @@ -25,7 +25,8 @@ endif() if(CONFIG_COVERAGE_MINI AND CONFIG_ARCH_TOOLCHAIN_GCC) nuttx_add_system_library(libcoverage) - target_compile_options(libcoverage PRIVATE -fno-profile-arcs - -fno-test-coverage) + target_compile_options( + libcoverage PRIVATE -fno-profile-arcs -fno-test-coverage + -fno-stack-protector) target_sources(libcoverage PRIVATE gcov.c) endif() From 0d05c2eee75797d6ed5db1f43c401f83b509f96c Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Thu, 2 Jan 2025 21:52:43 +0800 Subject: [PATCH 10/11] gcov: add reboot gcov storage coverage info Signed-off-by: wangmingrong1 --- boards/boardctl.c | 5 ++-- libs/libbuiltin/Kconfig | 19 ++++++++++++++ libs/libbuiltin/libgcc/gcov.c | 47 ++++++++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/boards/boardctl.c b/boards/boardctl.c index 9e652560d6c..4b882efc609 100644 --- a/boards/boardctl.c +++ b/boards/boardctl.c @@ -24,11 +24,12 @@ #include -#include #include +#include +#include #include #include -#include +#include #include #include diff --git a/libs/libbuiltin/Kconfig b/libs/libbuiltin/Kconfig index 4d8c581ce4f..bdcc0539e5b 100644 --- a/libs/libbuiltin/Kconfig +++ b/libs/libbuiltin/Kconfig @@ -65,6 +65,25 @@ config COVERAGE_NONE endchoice +config COVERAGE_GCOV_DUMP_REBOOT + bool "Dump gcov data on reboot" + default n + ---help--- + Dump gcov data on reboot, this will cause the gcov data to be + dumped to the default path when the system is rebooted. + +config COVERAGE_DEFAULT_PREFIX_STRIP + string "Default gcov dump path prefix strip" + default "99" + ---help--- + The default prefix strip of the gcov data + +config COVERAGE_DEFAULT_PREFIX + string "Default gcov dump path prefix" + default "/data" + ---help--- + The default prefix to store the gcov data + choice prompt "Builtin profile support" default PROFILE_NONE diff --git a/libs/libbuiltin/libgcc/gcov.c b/libs/libbuiltin/libgcc/gcov.c index 914fbdc50db..2b0edec71a4 100644 --- a/libs/libbuiltin/libgcc/gcov.c +++ b/libs/libbuiltin/libgcc/gcov.c @@ -22,15 +22,16 @@ * Included Files ****************************************************************************/ -#include #include +#include +#include #include #include -#include -#include #include +#include #include +#include /**************************************************************************** * Pre-processor Definitions @@ -246,7 +247,7 @@ static int gcov_process_path(FAR char *prefix, int strip, strcat(new_path, tokens[i]); if (access(new_path, F_OK) != 0) { - ret = mkdir(new_path, 0644); + ret = mkdir(new_path, 0777); if (ret != 0) { return -errno; @@ -297,12 +298,50 @@ static int gcov_write_file(FAR const char *filename, return ret; } +#ifdef CONFIG_COVERAGE_GCOV_DUMP_REBOOT +static int gcov_reboot_notify(FAR struct notifier_block *nb, + unsigned long action, FAR void *data) +{ + __gcov_dump(); + return 0; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ void __gcov_init(FAR struct gcov_info *info) { +#ifdef CONFIG_COVERAGE_GCOV_DUMP_REBOOT + static struct notifier_block nb; +#endif + char path[PATH_MAX] = CONFIG_COVERAGE_DEFAULT_PREFIX; + static int inited = 0; + struct tm *tm_info; + time_t cur; + + if (!inited) + { + cur = time(NULL); + tm_info = localtime(&cur); + + strftime(path + strlen(path), + PATH_MAX, + "/gcov_%Y%m%d_%H%M%S", + tm_info); + + setenv("GCOV_PREFIX_STRIP", CONFIG_COVERAGE_DEFAULT_PREFIX_STRIP, 1); + setenv("GCOV_PREFIX", path, 1); + +#ifdef CONFIG_COVERAGE_GCOV_DUMP_REBOOT + nb.notifier_call = gcov_reboot_notify; + register_reboot_notifier(&nb); +#endif + + inited++; + } + info->next = __gcov_info_start; __gcov_info_start = info; } From 8bda0206166ff322e2238da883ab874199ccce46 Mon Sep 17 00:00:00 2001 From: wangmingrong1 Date: Sun, 12 Jan 2025 14:05:05 +0800 Subject: [PATCH 11/11] ci: add ci environment tools Signed-off-by: wangmingrong1 --- .github/linters/setup.cfg | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/linters/setup.cfg diff --git a/.github/linters/setup.cfg b/.github/linters/setup.cfg new file mode 100644 index 00000000000..6ab13e1e45b --- /dev/null +++ b/.github/linters/setup.cfg @@ -0,0 +1,10 @@ +[flake8] +ignore = W503,W605,E203,E704 +max-complexity=27 +max-line-length =125 +multi line_output=3 +show-source = True +statistics = True + +[isort] +profile = black