tests: coverage: Add Gcov support.
This is a custom Gcov implementation. Taking excerpts from gcc gcc libgcc/libgcov.h and gcc/gcov-io.h. Ported to zephyr by Ramakrishna Pallala <ramakrishna.pallala@intel.com> and Adithya Baglody <adithya.nagaraj.baglody@intel.com> Signed-off-by: Adithya Baglody <adithya.nagaraj.baglody@intel.com>
This commit is contained in:
parent
40b8854705
commit
e223cfa9dd
6 changed files with 398 additions and 1 deletions
19
include/misc/gcov.h
Normal file
19
include/misc/gcov.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_INCLUDE_MISC_GCOV_H_
|
||||||
|
#define ZEPHYR_INCLUDE_MISC_GCOV_H_
|
||||||
|
|
||||||
|
#ifdef CONFIG_COVERAGE_GCOV
|
||||||
|
void gcov_coverage_dump(void);
|
||||||
|
void gcov_static_init(void);
|
||||||
|
#else
|
||||||
|
void gcov_coverage_dump(void) { }
|
||||||
|
void gcov_static_init(void) { }
|
||||||
|
|
||||||
|
#endif /* CONFIG_COVERAGE */
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_INCLUDE_MISC_GCOV_H_ */
|
|
@ -3,3 +3,4 @@ add_subdirectory_if_kconfig(ztest)
|
||||||
zephyr_include_directories_ifdef(CONFIG_TEST
|
zephyr_include_directories_ifdef(CONFIG_TEST
|
||||||
$ENV{ZEPHYR_BASE}/tests/include
|
$ENV{ZEPHYR_BASE}/tests/include
|
||||||
)
|
)
|
||||||
|
add_subdirectory_ifdef(CONFIG_COVERAGE_GCOV coverage)
|
||||||
|
|
|
@ -31,12 +31,25 @@ config TEST_EXTRA_STACKSIZE
|
||||||
|
|
||||||
config COVERAGE
|
config COVERAGE
|
||||||
bool "Create coverage data"
|
bool "Create coverage data"
|
||||||
depends on NATIVE_APPLICATION
|
default n
|
||||||
help
|
help
|
||||||
This option will build your application with the -coverage option
|
This option will build your application with the -coverage option
|
||||||
which will generate data that can be used to create coverage reports.
|
which will generate data that can be used to create coverage reports.
|
||||||
Currently this is fully supported only on the native POSIX port.
|
Currently this is fully supported only on the native POSIX port.
|
||||||
|
|
||||||
|
if COVERAGE
|
||||||
|
config COVERAGE_GCOV
|
||||||
|
bool "Create Coverage data from hardware platform"
|
||||||
|
select NEWLIB_LIBC
|
||||||
|
depends on !NATIVE_APPLICATION
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
This option will select the custom gcov library. The reports will
|
||||||
|
be available over serial. This serial dump can be passed to
|
||||||
|
gen_gcov_files.py which creates the required .gcda files. These
|
||||||
|
can be read by gcov utility. For more details see gcovr.com .
|
||||||
|
endif
|
||||||
|
|
||||||
config TEST_USERSPACE
|
config TEST_USERSPACE
|
||||||
bool "Enable userspace if available"
|
bool "Enable userspace if available"
|
||||||
depends on ARCH_HAS_USERSPACE
|
depends on ARCH_HAS_USERSPACE
|
||||||
|
|
1
tests/coverage/CMakeLists.txt
Normal file
1
tests/coverage/CMakeLists.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
zephyr_sources_ifdef(CONFIG_COVERAGE_GCOV coverage.c)
|
269
tests/coverage/coverage.c
Normal file
269
tests/coverage/coverage.c
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "coverage.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_X86
|
||||||
|
#define MALLOC_MAX_HEAP_SIZE 32768
|
||||||
|
#define MALLOC_MIN_BLOCK_SIZE 128
|
||||||
|
#else
|
||||||
|
#define MALLOC_MAX_HEAP_SIZE 16384
|
||||||
|
#define MALLOC_MIN_BLOCK_SIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
K_MEM_POOL_DEFINE(gcov_heap_mem_pool,
|
||||||
|
MALLOC_MIN_BLOCK_SIZE,
|
||||||
|
MALLOC_MAX_HEAP_SIZE, 1, 4);
|
||||||
|
|
||||||
|
|
||||||
|
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. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* buff_write_u64 - Store 64 bit data on a buffer and return the size
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MASK_32BIT (0xffffffffUL)
|
||||||
|
static inline void buff_write_u64(void *buffer, size_t *off, uint64_t v)
|
||||||
|
{
|
||||||
|
*((u32_t *)((u8_t *)buffer + *off) + 0) = (u32_t)(v & MASK_32BIT);
|
||||||
|
*((u32_t *)((u8_t *)buffer + *off) + 1) = (u32_t)((v >> 32) &
|
||||||
|
MASK_32BIT);
|
||||||
|
*off = *off + sizeof(u64_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* buff_write_u32 - Store 32 bit data on a buffer and return the size
|
||||||
|
*/
|
||||||
|
static inline void buff_write_u32(void *buffer, size_t *off, u32_t v)
|
||||||
|
{
|
||||||
|
*((u32_t *)((u8_t *)buffer + *off)) = v;
|
||||||
|
*off = *off + sizeof(u32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t calculate_buff_size(struct gcov_info *info)
|
||||||
|
{
|
||||||
|
u32_t iter;
|
||||||
|
u32_t iter_1;
|
||||||
|
u32_t iter_2;
|
||||||
|
/* few Fixed values at the start version, stamp and magic number. */
|
||||||
|
u32_t size = sizeof(u32_t) * 3;
|
||||||
|
|
||||||
|
for (iter = 0; iter < info->n_functions; iter++) {
|
||||||
|
/* space for TAG_FUNCTION and FUNCTION_LENGTH
|
||||||
|
* ident
|
||||||
|
* lineno_checksum
|
||||||
|
* cfg_checksum
|
||||||
|
*/
|
||||||
|
size += (sizeof(u32_t) * 5);
|
||||||
|
|
||||||
|
for (iter_1 = 0; iter_1 < GCOV_COUNTERS; iter_1++) {
|
||||||
|
if (!info->merge[iter_1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for function counter and number of values */
|
||||||
|
size += (sizeof(u32_t) * 2);
|
||||||
|
|
||||||
|
for (iter_2 = 0;
|
||||||
|
iter_2 < info->functions[iter]->ctrs->num;
|
||||||
|
iter_2++) {
|
||||||
|
|
||||||
|
/* Iter for values which is u64_t */
|
||||||
|
size += (sizeof(u64_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
size_t populate_buffer(u8_t *buffer, struct gcov_info *info)
|
||||||
|
{
|
||||||
|
struct gcov_fn_info *functions;
|
||||||
|
struct gcov_ctr_info *counters_per_func;
|
||||||
|
u32_t iter_functions;
|
||||||
|
u32_t iter_counts;
|
||||||
|
u32_t iter_counter_values;
|
||||||
|
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);
|
||||||
|
|
||||||
|
for (iter_functions = 0;
|
||||||
|
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;
|
||||||
|
|
||||||
|
for (iter_counts = 0;
|
||||||
|
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));
|
||||||
|
|
||||||
|
buff_write_u32(buffer,
|
||||||
|
&buffer_write_position,
|
||||||
|
counters_per_func->num * 2);
|
||||||
|
|
||||||
|
for (iter_counter_values = 0;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
u32_t iter;
|
||||||
|
|
||||||
|
printk("\n%c", FILE_START_INDICATOR);
|
||||||
|
while (*filename != '\0') {
|
||||||
|
printk("%c", *filename++);
|
||||||
|
}
|
||||||
|
printk("%c", GCOV_DUMP_SEPARATOR);
|
||||||
|
|
||||||
|
/* Data dump */
|
||||||
|
|
||||||
|
for (iter = 0; iter < len; iter++) {
|
||||||
|
printk(" %02x", (u8_t)*ptr++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves gcov coverage data and sends it over the given interface.
|
||||||
|
*/
|
||||||
|
void gcov_coverage_dump(void)
|
||||||
|
{
|
||||||
|
u8_t *buffer;
|
||||||
|
size_t size;
|
||||||
|
size_t written_size;
|
||||||
|
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);
|
||||||
|
|
||||||
|
buffer = (u8_t *) k_mem_pool_malloc(&gcov_heap_mem_pool, size);
|
||||||
|
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);
|
||||||
|
|
||||||
|
k_free(buffer);
|
||||||
|
gcov_list = gcov_list->next;
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
extern u32_t __init_array_start, __init_array_end;
|
||||||
|
u32_t func_pointer_start = (u32_t) &__init_array_start;
|
||||||
|
u32_t func_pointer_end = (u32_t) &__init_array_end;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
94
tests/coverage/coverage.h
Normal file
94
tests/coverage/coverage.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/* Copyright (C) 1996-2018 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This file is part of GCC.
|
||||||
|
|
||||||
|
GCC is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation; either version 3, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
Under Section 7 of GPL version 3, you are granted additional
|
||||||
|
permissions described in the GCC Runtime Library Exception, version
|
||||||
|
3.1, as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License and
|
||||||
|
a copy of the GCC Runtime Library Exception along with this program;
|
||||||
|
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file has excerpts from gcc libgcc/libgcov.h and gcc/gcov-io.h.
|
||||||
|
* Which is governed by section 7 of additional permissions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _COVERAGE_H_
|
||||||
|
#define _COVERAGE_H_
|
||||||
|
|
||||||
|
#define GCOV_COUNTERS 10U
|
||||||
|
|
||||||
|
typedef u64_t gcov_type;
|
||||||
|
|
||||||
|
#define GCOV_TAG_FUNCTION_LENGTH 3
|
||||||
|
#define GCOV_DATA_MAGIC (0x67636461)
|
||||||
|
#define GCOV_TAG_FUNCTION (0x01000000)
|
||||||
|
#define GCOV_TAG_COUNTER_BASE (0x01a10000)
|
||||||
|
#define GCOV_TAG_FOR_COUNTER(count) \
|
||||||
|
(GCOV_TAG_COUNTER_BASE + ((u32_t) (count) << 17))
|
||||||
|
|
||||||
|
#define FILE_START_INDICATOR '*'
|
||||||
|
#define GCOV_DUMP_SEPARATOR '<'
|
||||||
|
|
||||||
|
/**Information about counters for a single function
|
||||||
|
*
|
||||||
|
* This data is generated by gcc during compilation and doesn't change
|
||||||
|
* at run-time with the exception of the values array.
|
||||||
|
*/
|
||||||
|
struct gcov_ctr_info {
|
||||||
|
unsigned int num; /* number of counter values for this type */
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* Information about a single function. This uses the trailing array
|
||||||
|
* idiom. The number of counters is determined from the merge pointer
|
||||||
|
* array in gcov_info. The key is used to detect which of a set of
|
||||||
|
* comdat functions was selected -- it points to the gcov_info object
|
||||||
|
* of the object file containing the selected comdat function.
|
||||||
|
*/
|
||||||
|
struct gcov_fn_info {
|
||||||
|
const struct gcov_info *key; /* comdat key */
|
||||||
|
unsigned int ident; /* unique ident of function */
|
||||||
|
unsigned int lineno_checksum; /* function lineo_checksum */
|
||||||
|
unsigned int cfg_checksum; /* function cfg checksum */
|
||||||
|
struct gcov_ctr_info ctrs[0]; /* instrumented counters */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 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) */
|
||||||
|
struct gcov_info *next; /* List head for a singly-linked list */
|
||||||
|
unsigned int stamp; /* Uniquifying time stamp */
|
||||||
|
const char *filename; /* Name of the associated gcda data file */
|
||||||
|
/* merge functions, null for unused*/
|
||||||
|
void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
|
||||||
|
unsigned int n_functions; /* number of instrumented functions */
|
||||||
|
struct gcov_fn_info **functions; /* function information */
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _COVERAGE_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue