Skip to content

Commit

Permalink
gcov: Support for the most streamlined profile of LLVM-embedded-toolc…
Browse files Browse the repository at this point in the history
…hain-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]>
  • Loading branch information
W-M-R committed Nov 4, 2024
1 parent da4f4e5 commit b5dbe8b
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 0 deletions.
1 change: 1 addition & 0 deletions libs/libc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ source "libs/libc/string/Kconfig"
source "libs/libc/pthread/Kconfig"
source "libs/libc/dlfcn/Kconfig"
source "libs/libc/modlib/Kconfig"
source "libs/libc/gcov/Kconfig"
source "libs/libc/gdbstub/Kconfig"
source "libs/libc/grp/Kconfig"
source "libs/libc/pwd/Kconfig"
Expand Down
1 change: 1 addition & 0 deletions libs/libc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ include dlfcn/Make.defs
include errno/Make.defs
include eventfd/Make.defs
include fixedmath/Make.defs
include gcov/Make.defs
include gdbstub/Make.defs
include grp/Make.defs
include gnssutils/Make.defs
Expand Down
27 changes: 27 additions & 0 deletions libs/libc/gcov/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# ##############################################################################
# libs/libc/gcov/CMakeLists.txt
#
# 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.
#
# ##############################################################################

if(CONFIG_LIB_GCOV)
if(CONFIG_ARCH_TOOLCHAIN_CLANG)
target_sources(c PRIVATE lib_clang.c)
endif()
endif()
16 changes: 16 additions & 0 deletions libs/libc/gcov/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#

config LIB_GCOV
tristate "Enable GCOV support"
select HAVE_CXXINITIALIZE
default n
---help---
Enable GCOV support for code coverage analysis.
After turning on this option, code coverage can be analyzed.
If this option is enabled, image size and performance will be
significantly reduced. Before use, you need to add the
"-fprofile-instr-generate -fcoverage-mapping" compilation parameters
to the file to be analyzed.
34 changes: 34 additions & 0 deletions libs/libc/gcov/Make.defs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
############################################################################
# libs/libc/gcov/Make.defs
#
# 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.
#
############################################################################

# Add the fixed precision math C files to the build

ifeq ($(CONFIG_LIB_GCOV),y)
ifeq ($(CONFIG_ARCH_TOOLCHAIN_CLANG),y)
CSRCS += lib_clang.c
endif
endif

# Add the fixed precision math directory to the build

DEPPATH += --dep-path gcov
VPATH += :gcov
294 changes: 294 additions & 0 deletions libs/libc/gcov/lib_clang.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
/****************************************************************************
* libs/libc/gcov/lib_clang.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 <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.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 __attribute__((aligned(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 &__start__llvm_prf_data;
}

static const __llvm_profile_data *__llvm_profile_end_data(void)
{
return &__end__llvm_prf_data;
}

static const char *__llvm_profile_begin_names(void)
{
return &__start__llvm_prf_names;
}

static const char *__llvm_profile_end_names(void)
{
return &__end__llvm_prf_names;
}

static char *__llvm_profile_begin_counters(void)
{
return &__start__llvm_prf_cnts;
}

static char *__llvm_profile_end_counters(void)
{
return &__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)
{
FILE *fd;

/* 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 = fopen(filename, "wb");
if (!fd)
{
perror("fopen default.profraw failed");
return;
}

/* Header */

fwrite(&hdr, sizeof(hdr), 1, fd);

/* Data */

fwrite(data_begin, sizeof(__llvm_profile_data), num_data, fd);

/* Counters */

fwrite(counters_begin, sizeof(uint64_t), num_counters, fd);

/* Names */

fwrite(names_begin, sizeof(uint8_t), names_size, fd);

/* Padding */

for (; padding_bytes_after_names != 0; --padding_bytes_after_names)
{
fputc(0, fd);
}

fclose(fd);
}

0 comments on commit b5dbe8b

Please sign in to comment.