2018-08-29 17:31:46 +05:30
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Intel Corporation
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
includes: prefer <zephyr/kernel.h> over <zephyr/zephyr.h>
As of today <zephyr/zephyr.h> is 100% equivalent to <zephyr/kernel.h>.
This patch proposes to then include <zephyr/kernel.h> instead of
<zephyr/zephyr.h> since it is more clear that you are including the
Kernel APIs and (probably) nothing else. <zephyr/zephyr.h> sounds like a
catch-all header that may be confusing. Most applications need to
include a bunch of other things to compile, e.g. driver headers or
subsystem headers like BT, logging, etc.
The idea of a catch-all header in Zephyr is probably not feasible
anyway. Reason is that Zephyr is not a library, like it could be for
example `libpython`. Zephyr provides many utilities nowadays: a kernel,
drivers, subsystems, etc and things will likely grow. A catch-all header
would be massive, difficult to keep up-to-date. It is also likely that
an application will only build a small subset. Note that subsystem-level
headers may use a catch-all approach to make things easier, though.
NOTE: This patch is **NOT** removing the header, just removing its usage
in-tree. I'd advocate for its deprecation (add a #warning on it), but I
understand many people will have concerns.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2022-08-25 09:58:46 +02:00
|
|
|
#include <zephyr/kernel.h>
|
2018-08-29 17:31:46 +05:30
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <errno.h>
|
2023-01-16 14:35:35 +01:00
|
|
|
#include <string.h>
|
2018-08-29 17:31:46 +05:30
|
|
|
#include "coverage.h"
|
|
|
|
|
2022-11-04 22:44:09 -04:00
|
|
|
K_HEAP_DEFINE(gcov_heap, CONFIG_COVERAGE_GCOV_HEAP_SIZE);
|
2018-08-29 17:31:46 +05:30
|
|
|
|
|
|
|
static struct gcov_info *gcov_info_head;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is called by gcc-generated constructor code for each object file compiled
|
|
|
|
* with -fprofile-arcs.
|
|
|
|
*/
|
|
|
|
void __gcov_init(struct gcov_info *info)
|
|
|
|
{
|
|
|
|
info->next = gcov_info_head;
|
|
|
|
gcov_info_head = info;
|
|
|
|
}
|
|
|
|
|
|
|
|
void __gcov_merge_add(gcov_type *counters, unsigned int n_counters)
|
|
|
|
{
|
|
|
|
/* Unused. */
|
|
|
|
}
|
|
|
|
|
2019-04-01 15:57:36 -07:00
|
|
|
void __gcov_exit(void)
|
|
|
|
{
|
|
|
|
/* Unused. */
|
|
|
|
}
|
|
|
|
|
2018-08-29 17:31:46 +05:30
|
|
|
/**
|
|
|
|
* buff_write_u64 - Store 64 bit data on a buffer and return the size
|
|
|
|
*/
|
|
|
|
|
|
|
|
static inline void buff_write_u64(void *buffer, size_t *off, uint64_t v)
|
|
|
|
{
|
2023-01-16 14:35:35 +01:00
|
|
|
memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v));
|
2020-05-27 11:26:57 -05:00
|
|
|
*off = *off + sizeof(uint64_t);
|
2018-08-29 17:31:46 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* buff_write_u32 - Store 32 bit data on a buffer and return the size
|
|
|
|
*/
|
2020-05-27 11:26:57 -05:00
|
|
|
static inline void buff_write_u32(void *buffer, size_t *off, uint32_t v)
|
2018-08-29 17:31:46 +05:30
|
|
|
{
|
2023-01-16 14:35:35 +01:00
|
|
|
memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v));
|
2020-05-27 11:26:57 -05:00
|
|
|
*off = *off + sizeof(uint32_t);
|
2018-08-29 17:31:46 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t calculate_buff_size(struct gcov_info *info)
|
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t iter;
|
|
|
|
uint32_t iter_1;
|
|
|
|
uint32_t iter_2;
|
2022-09-21 17:26:25 +08:00
|
|
|
|
|
|
|
/* Few fixed values at the start: magic number,
|
|
|
|
* version, stamp, and checksum.
|
|
|
|
*/
|
|
|
|
#ifdef GCOV_12_FORMAT
|
|
|
|
uint32_t size = sizeof(uint32_t) * 4;
|
|
|
|
#else
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t size = sizeof(uint32_t) * 3;
|
2022-09-21 17:26:25 +08:00
|
|
|
#endif
|
2018-08-29 17:31:46 +05:30
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
for (iter = 0U; iter < info->n_functions; iter++) {
|
2018-08-29 17:31:46 +05:30
|
|
|
/* space for TAG_FUNCTION and FUNCTION_LENGTH
|
|
|
|
* ident
|
|
|
|
* lineno_checksum
|
|
|
|
* cfg_checksum
|
|
|
|
*/
|
2020-05-27 11:26:57 -05:00
|
|
|
size += (sizeof(uint32_t) * 5);
|
2018-08-29 17:31:46 +05:30
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
for (iter_1 = 0U; iter_1 < GCOV_COUNTERS; iter_1++) {
|
2018-08-29 17:31:46 +05:30
|
|
|
if (!info->merge[iter_1]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for function counter and number of values */
|
2020-05-27 11:26:57 -05:00
|
|
|
size += (sizeof(uint32_t) * 2);
|
2018-08-29 17:31:46 +05:30
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
for (iter_2 = 0U;
|
2018-08-29 17:31:46 +05:30
|
|
|
iter_2 < info->functions[iter]->ctrs->num;
|
|
|
|
iter_2++) {
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
/* Iter for values which is uint64_t */
|
|
|
|
size += (sizeof(uint64_t));
|
2018-08-29 17:31:46 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* populate_buffer - convert from gcov data set (info) to
|
|
|
|
* .gcda file format.
|
|
|
|
* This buffer will now have info similar to a regular gcda
|
|
|
|
* format.
|
|
|
|
*/
|
2020-05-27 11:26:57 -05:00
|
|
|
size_t populate_buffer(uint8_t *buffer, struct gcov_info *info)
|
2018-08-29 17:31:46 +05:30
|
|
|
{
|
|
|
|
struct gcov_fn_info *functions;
|
|
|
|
struct gcov_ctr_info *counters_per_func;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t iter_functions;
|
|
|
|
uint32_t iter_counts;
|
|
|
|
uint32_t iter_counter_values;
|
2018-08-29 17:31:46 +05:30
|
|
|
size_t buffer_write_position = 0;
|
|
|
|
|
|
|
|
/* File header. */
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
GCOV_DATA_MAGIC);
|
|
|
|
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
info->version);
|
|
|
|
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
info->stamp);
|
|
|
|
|
2022-09-21 17:26:25 +08:00
|
|
|
#ifdef GCOV_12_FORMAT
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
info->checksum);
|
|
|
|
#endif
|
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
for (iter_functions = 0U;
|
2018-08-29 17:31:46 +05:30
|
|
|
iter_functions < info->n_functions;
|
|
|
|
iter_functions++) {
|
|
|
|
|
|
|
|
functions = info->functions[iter_functions];
|
|
|
|
|
|
|
|
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
GCOV_TAG_FUNCTION);
|
|
|
|
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
GCOV_TAG_FUNCTION_LENGTH);
|
|
|
|
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
functions->ident);
|
|
|
|
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
functions->lineno_checksum);
|
|
|
|
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
functions->cfg_checksum);
|
|
|
|
|
|
|
|
counters_per_func = functions->ctrs;
|
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
for (iter_counts = 0U;
|
2018-08-29 17:31:46 +05:30
|
|
|
iter_counts < GCOV_COUNTERS;
|
|
|
|
iter_counts++) {
|
|
|
|
|
|
|
|
if (!info->merge[iter_counts]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
GCOV_TAG_FOR_COUNTER(iter_counts));
|
|
|
|
|
2022-09-21 17:26:25 +08:00
|
|
|
#ifdef GCOV_12_FORMAT
|
|
|
|
/* GCOV 12 counts the length by bytes */
|
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
counters_per_func->num * 2U * 4);
|
|
|
|
#else
|
2018-08-29 17:31:46 +05:30
|
|
|
buff_write_u32(buffer,
|
|
|
|
&buffer_write_position,
|
2019-03-26 19:57:45 -06:00
|
|
|
counters_per_func->num * 2U);
|
2022-09-21 17:26:25 +08:00
|
|
|
#endif
|
2018-08-29 17:31:46 +05:30
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
for (iter_counter_values = 0U;
|
2018-08-29 17:31:46 +05:30
|
|
|
iter_counter_values < counters_per_func->num;
|
|
|
|
iter_counter_values++) {
|
|
|
|
|
|
|
|
buff_write_u64(buffer,
|
|
|
|
&buffer_write_position,
|
|
|
|
counters_per_func->\
|
|
|
|
values[iter_counter_values]);
|
|
|
|
}
|
|
|
|
|
|
|
|
counters_per_func++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buffer_write_position;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dump_on_console(const char *filename, char *ptr, size_t len)
|
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t iter;
|
2018-08-29 17:31:46 +05:30
|
|
|
|
|
|
|
printk("\n%c", FILE_START_INDICATOR);
|
|
|
|
while (*filename != '\0') {
|
|
|
|
printk("%c", *filename++);
|
|
|
|
}
|
|
|
|
printk("%c", GCOV_DUMP_SEPARATOR);
|
|
|
|
|
|
|
|
/* Data dump */
|
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
for (iter = 0U; iter < len; iter++) {
|
2020-05-27 11:26:57 -05:00
|
|
|
printk(" %02x", (uint8_t)*ptr++);
|
2018-08-29 17:31:46 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves gcov coverage data and sends it over the given interface.
|
|
|
|
*/
|
|
|
|
void gcov_coverage_dump(void)
|
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t *buffer;
|
2018-08-29 17:31:46 +05:30
|
|
|
size_t size;
|
|
|
|
size_t written_size;
|
2020-12-28 16:51:57 +01:00
|
|
|
struct gcov_info *gcov_list_first = gcov_info_head;
|
2018-08-29 17:31:46 +05:30
|
|
|
struct gcov_info *gcov_list = gcov_info_head;
|
|
|
|
|
|
|
|
k_sched_lock();
|
|
|
|
printk("\nGCOV_COVERAGE_DUMP_START");
|
|
|
|
while (gcov_list) {
|
|
|
|
|
|
|
|
size = calculate_buff_size(gcov_list);
|
|
|
|
|
2020-12-07 05:53:28 -08:00
|
|
|
buffer = k_heap_alloc(&gcov_heap, size, K_NO_WAIT);
|
2018-08-29 17:31:46 +05:30
|
|
|
if (!buffer) {
|
|
|
|
printk("No Mem available to continue dump\n");
|
|
|
|
goto coverage_dump_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
written_size = populate_buffer(buffer, gcov_list);
|
|
|
|
if (written_size != size) {
|
|
|
|
printk("Write Error on buff\n");
|
|
|
|
goto coverage_dump_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
dump_on_console(gcov_list->filename, buffer, size);
|
|
|
|
|
2020-12-07 05:53:28 -08:00
|
|
|
k_heap_free(&gcov_heap, buffer);
|
2018-08-29 17:31:46 +05:30
|
|
|
gcov_list = gcov_list->next;
|
2020-12-28 16:51:57 +01:00
|
|
|
if (gcov_list_first == gcov_list) {
|
|
|
|
goto coverage_dump_end;
|
|
|
|
}
|
2018-08-29 17:31:46 +05:30
|
|
|
}
|
|
|
|
coverage_dump_end:
|
|
|
|
printk("\nGCOV_COVERAGE_DUMP_END\n");
|
|
|
|
k_sched_unlock();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Initialize the gcov by calling the required constructors */
|
|
|
|
void gcov_static_init(void)
|
|
|
|
{
|
2020-12-04 23:09:33 +08:00
|
|
|
extern uintptr_t __init_array_start, __init_array_end;
|
|
|
|
uintptr_t func_pointer_start = (uintptr_t) &__init_array_start;
|
|
|
|
uintptr_t func_pointer_end = (uintptr_t) &__init_array_end;
|
2018-08-29 17:31:46 +05:30
|
|
|
|
|
|
|
while (func_pointer_start < func_pointer_end) {
|
|
|
|
void (**p)(void);
|
|
|
|
/* get function pointer */
|
|
|
|
p = (void (**)(void)) func_pointer_start;
|
|
|
|
(*p)();
|
|
|
|
func_pointer_start += sizeof(p);
|
|
|
|
}
|
|
|
|
}
|