345 lines
11 KiB
C
345 lines
11 KiB
C
/**********************************************************************/
|
|
/** @addtogroup embedded_gcov
|
|
* @{
|
|
* @file
|
|
* @version $Id: $
|
|
*
|
|
* @author 2021-08-31 kjpeters Working and cleaned up version.
|
|
*
|
|
* @note Based on GCOV-related code of the Linux kernel,
|
|
* as described online by Thanassis Tsiodras (April 2016)
|
|
* https://www.thanassis.space/coverage.html
|
|
* and by Alexander Tarasikov
|
|
* http://allsoftwaresucks.blogspot.com/2015/05/gcov-is-amazing-yet-undocumented.html
|
|
* with additional investigation, updating, cleanup, and portability
|
|
* by Ken Peters.
|
|
*
|
|
* @brief Private source file for embedded gcov.
|
|
*
|
|
**********************************************************************/
|
|
/*
|
|
* This code provides functions to handle gcc's profiling data format
|
|
* introduced with gcc 4.7.
|
|
*
|
|
* This file is based heavily on gcc_3_4.c file.
|
|
*
|
|
* For a better understanding, refer to gcc source:
|
|
* gcc/gcov-io.h
|
|
* libgcc/libgcov.c
|
|
*
|
|
* Uses gcc-internal data definitions.
|
|
*/
|
|
|
|
#include "gcov_gcc.h"
|
|
|
|
#ifdef GCOV_OPT_RESET_WATCHDOG
|
|
/* In an embedded system, you might want to reset any watchdog timer below, */
|
|
/* depending on your timeout versus gcov tree size */
|
|
/* If so, include any needed header files. */
|
|
#include "all.h"
|
|
#include "defs.h"
|
|
#endif // GCOV_OPT_RESET_WATCHDOG
|
|
|
|
|
|
/**
|
|
* struct gcov_ctr_info - information about counters for a single function
|
|
* @num: number of counter values for this type
|
|
* @values: array of counter values for this type
|
|
*
|
|
* This data is generated by gcc during compilation and doesn't change
|
|
* at run-time with the exception of the values array.
|
|
*/
|
|
/* Compare to libgcc/libgcov.h */
|
|
struct gcov_ctr_info {
|
|
gcov_unsigned_t num;
|
|
gcov_type *values;
|
|
};
|
|
|
|
/**
|
|
* struct gcov_fn_info - profiling meta data per function
|
|
* @key: comdat key
|
|
* @ident: unique ident of function
|
|
* @lineno_checksum: function lineo_checksum
|
|
* @cfg_checksum: function cfg checksum
|
|
* @ctrs: instrumented counters
|
|
*
|
|
* 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.
|
|
*/
|
|
/* Compare to libgcc/libgcov.h */
|
|
struct gcov_fn_info {
|
|
const struct gcov_info *key;
|
|
gcov_unsigned_t ident;
|
|
gcov_unsigned_t lineno_checksum;
|
|
gcov_unsigned_t cfg_checksum;
|
|
struct gcov_ctr_info ctrs[1];
|
|
};
|
|
|
|
/* Type of function used to merge counters. */
|
|
/* Compare to libgcc/libgcov.h */
|
|
typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
|
|
|
|
/**
|
|
* struct gcov_info - profiling data per object file
|
|
* @version: gcov version magic indicating the gcc version used for compilation
|
|
* @next: list head for a singly-linked list
|
|
* @stamp: uniquifying time stamp
|
|
* @filename: name of the associated gcov data file
|
|
* @merge: merge functions (null for unused counter type)
|
|
* @n_functions: number of instrumented functions
|
|
* @functions: pointer to pointers to function information
|
|
*
|
|
* This data is generated by gcc during compilation and doesn't change
|
|
* at run-time with the exception of the next pointer.
|
|
*/
|
|
/* Compare to libgcc/libgcov.h */
|
|
struct gcov_info {
|
|
gcov_unsigned_t version;
|
|
struct gcov_info *next;
|
|
gcov_unsigned_t stamp;
|
|
const char *filename;
|
|
gcov_merge_fn merge[GCOV_COUNTERS];
|
|
unsigned n_functions;
|
|
struct gcov_fn_info **functions;
|
|
};
|
|
|
|
/**
|
|
* gcov_info_filename - return info filename
|
|
* @info: profiling data set
|
|
*
|
|
* Need this to access opaque gcov_info to get filename in public code.
|
|
*/
|
|
const char *gcov_info_filename(struct gcov_info *info)
|
|
{
|
|
return info->filename;
|
|
}
|
|
|
|
/* See gcc/gcov-io.h for description of number formats */
|
|
|
|
/**
|
|
* store_gcov_unsigned - store 32 bit number in gcov format to buffer
|
|
* @buffer: target buffer or NULL
|
|
* @off: offset into the buffer
|
|
* @v: value to be stored
|
|
*
|
|
* Number format defined by gcc: numbers are recorded in the 32 bit
|
|
* unsigned binary form of the endianness of the machine generating the
|
|
* file. Returns the number of bytes stored. If @buffer is %NULL, doesn't
|
|
* store anything.
|
|
*/
|
|
/* Slightly like gcc/gcov-io.c function gcov_write_unsigned() (1-word item) */
|
|
/* Need buffer to be 32-bit-aligned for type-safe internal usage */
|
|
static size_t store_gcov_unsigned(gcov_unsigned_t *buffer, size_t off, gcov_unsigned_t v)
|
|
{
|
|
gcov_unsigned_t *data;
|
|
|
|
if (buffer) {
|
|
data = buffer + off;
|
|
*data = v;
|
|
}
|
|
|
|
/* return count of buffer data type units */
|
|
return sizeof(*data)/sizeof(*buffer);
|
|
}
|
|
|
|
/**
|
|
* store_gcov_tag_length - 32 bit tag and 32 bit length in gcov format to buffer
|
|
* @buffer: target buffer or NULL
|
|
* @off: offset into the buffer
|
|
* @tag: tag value to be stored
|
|
* @length: length value to be stored
|
|
*
|
|
* Number format defined by gcc: numbers are recorded in the 32 bit
|
|
* unsigned binary form of the endianness of the machine generating the
|
|
* file. 64 bit numbers are stored as two 32 bit numbers, the low part
|
|
* first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store
|
|
* anything.
|
|
*/
|
|
/* Slightly like gcc/gcov-io.c function gcov_write_tag_length() (1-word tag and 1-word length) */
|
|
/* or gcov_write_tag() (1-word tag and implied 1-word "length = 0") */
|
|
/* Need buffer to be 32-bit-aligned for type-safe internal usage */
|
|
static size_t store_gcov_tag_length(gcov_unsigned_t *buffer, size_t off, gcov_unsigned_t tag, gcov_unsigned_t length)
|
|
{
|
|
gcov_unsigned_t *data;
|
|
|
|
if (buffer) {
|
|
data = buffer + off;
|
|
|
|
data[0] = tag;
|
|
data[1] = length;
|
|
}
|
|
|
|
/* return count of buffer data type units */
|
|
return sizeof(*data)/sizeof(*buffer) * 2;
|
|
}
|
|
|
|
/**
|
|
* store_gcov_counter - store 64 bit number in gcov format to buffer
|
|
* @buffer: target buffer or NULL
|
|
* @off: offset into the buffer
|
|
* @v: value to be stored
|
|
*
|
|
* Number format defined by gcc: numbers are recorded in the 32 bit
|
|
* unsigned binary form of the endianness of the machine generating the
|
|
* file. 64 bit numbers are stored as two 32 bit numbers, the low part
|
|
* first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store
|
|
* anything.
|
|
*/
|
|
/* Slightly like gcc/gcov-io.c function gcov_write_counter() (2-word item) */
|
|
/* Need buffer to be 32-bit-aligned for type-safe internal usage */
|
|
static size_t store_gcov_counter(gcov_unsigned_t *buffer, size_t off, gcov_type v)
|
|
{
|
|
gcov_unsigned_t *data;
|
|
|
|
if (buffer) {
|
|
data = buffer + off;
|
|
|
|
data[0] = (v & 0xffffffffUL);
|
|
data[1] = (v >> 32);
|
|
}
|
|
|
|
/* return count of buffer data type units */
|
|
return sizeof(*data)/sizeof(*buffer) * 2;
|
|
}
|
|
|
|
/**
|
|
* gcov_convert_to_gcda - convert profiling data set to gcda file format
|
|
* @buffer: the buffer to store file data or %NULL if no data should be stored
|
|
* @info: profiling data set to be converted
|
|
*
|
|
* Returns the number of bytes that were/would have been stored into the buffer.
|
|
*/
|
|
/* Our own creation, but compare to libgcc/libgcov-driver.c function write_one_data() */
|
|
/* Need buffer to be 32-bit-aligned for type-safe internal usage */
|
|
size_t gcov_convert_to_gcda(gcov_unsigned_t *buffer, struct gcov_info *gi_ptr)
|
|
{
|
|
const struct gcov_fn_info *fi_ptr;
|
|
const struct gcov_ctr_info *ci_ptr;
|
|
unsigned int fi_idx;
|
|
unsigned int ct_idx;
|
|
unsigned int cv_idx;
|
|
size_t pos = 0; /* offset in buffer, in buffer data type units */
|
|
|
|
/* File header. */
|
|
pos += store_gcov_tag_length(buffer, pos, GCOV_DATA_MAGIC, gi_ptr->version);
|
|
pos += store_gcov_unsigned(buffer, pos, gi_ptr->stamp);
|
|
|
|
/* Write execution counts for each function. */
|
|
for (fi_idx = 0; fi_idx < gi_ptr->n_functions; fi_idx++) {
|
|
fi_ptr = gi_ptr->functions[fi_idx];
|
|
|
|
#ifdef GCOV_OPT_RESET_WATCHDOG
|
|
/* In an embedded system, you might want to reset any watchdog timer here, */
|
|
/* depending on your timeout versus gcov tree size */
|
|
SP_WDG = WATCHDOG_RESET;
|
|
#endif // GCOV_OPT_RESET_WATCHDOG
|
|
|
|
/* Function record. */
|
|
pos += store_gcov_tag_length(buffer, pos, GCOV_TAG_FUNCTION, GCOV_TAG_FUNCTION_LENGTH);
|
|
|
|
pos += store_gcov_unsigned(buffer, pos, fi_ptr->ident);
|
|
pos += store_gcov_unsigned(buffer, pos, fi_ptr->lineno_checksum);
|
|
pos += store_gcov_unsigned(buffer, pos, fi_ptr->cfg_checksum);
|
|
|
|
ci_ptr = fi_ptr->ctrs;
|
|
|
|
for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
|
|
if (!gi_ptr->merge[ct_idx]) {
|
|
/* Unused counter */
|
|
continue;
|
|
}
|
|
|
|
/* Counter record. */
|
|
pos += store_gcov_tag_length(buffer, pos,
|
|
GCOV_TAG_FOR_COUNTER(ct_idx),
|
|
GCOV_TAG_COUNTER_LENGTH(ci_ptr->num));
|
|
|
|
for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
|
|
pos += store_gcov_counter(buffer, pos,
|
|
ci_ptr->values[cv_idx]);
|
|
}
|
|
ci_ptr++;
|
|
}
|
|
}
|
|
|
|
/* return count of bytes (convert from count of buffer data type units) */
|
|
return pos * sizeof(*buffer);
|
|
}
|
|
|
|
/**
|
|
* gcov_clear_counters - set profiling counters to zero
|
|
* @info: profiling data set to be cleared
|
|
*
|
|
*/
|
|
/* Our own creation, but compare to libgcc/libgcov-driver.c function write_one_data() */
|
|
void gcov_clear_counters(struct gcov_info *gi_ptr)
|
|
{
|
|
const struct gcov_fn_info *fi_ptr;
|
|
const struct gcov_ctr_info *ci_ptr;
|
|
unsigned int fi_idx;
|
|
unsigned int ct_idx;
|
|
unsigned int cv_idx;
|
|
|
|
/* Clear execution counts for each function. */
|
|
for (fi_idx = 0; fi_idx < gi_ptr->n_functions; fi_idx++) {
|
|
fi_ptr = gi_ptr->functions[fi_idx];
|
|
|
|
ci_ptr = fi_ptr->ctrs;
|
|
|
|
for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
|
|
if (!gi_ptr->merge[ct_idx]) {
|
|
/* Unused counter */
|
|
continue;
|
|
}
|
|
|
|
/* Counter record. */
|
|
for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
|
|
ci_ptr->values[cv_idx] = 0;
|
|
}
|
|
ci_ptr++;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/** @}
|
|
*/
|
|
/*
|
|
* embedded-gcov gcov_gcc.c gcov internals interface code
|
|
*
|
|
* Copyright (c) 2021 California Institute of Technology (“Caltech”).
|
|
* U.S. Government sponsorship acknowledged.
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* Neither the name of Caltech nor its operating division, the Jet Propulsion Laboratory,
|
|
* nor the names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|