diff options
Diffstat (limited to 'src/include/usr/gcov.h')
-rw-r--r-- | src/include/usr/gcov.h | 216 |
1 files changed, 190 insertions, 26 deletions
diff --git a/src/include/usr/gcov.h b/src/include/usr/gcov.h index ede5e547b..8b506a935 100644 --- a/src/include/usr/gcov.h +++ b/src/include/usr/gcov.h @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2012,2014 */ +/* Contributors Listed Below - COPYRIGHT 2012,2019 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -39,6 +41,7 @@ #include <stddef.h> #include <stdint.h> #include <string.h> +#include <arch/ppc.H> /** @struct gcov_info * @brief Structure generated by gcc. Do not use. @@ -56,23 +59,109 @@ * aligned on a 64-bit boundary. The unusedN fields are to ensure proper * alignment. */ + +// Inferred settings +#define BITS_PER_UNIT 8 +#define LONG_LONG_TYPE_SIZE 64 + +#define _STRINGIFY(X) #X +#define STRINGIFY(X) _STRINGIFY(X) + +static_assert(sizeof(long long) * 8 == LONG_LONG_TYPE_SIZE, + "sizeof(long long) * 8 must be LONG_LONG_TYPE_SIZE (" + STRINGIFY(LONG_LONG_TYPE_SIZE) + ")"); + +/* The following typedefs and structures were taken from: + * https://github.com/gcc-mirror/gcc/blob/gcc-4_9-branch/libgcc/libgcov.h + */ + +#if BITS_PER_UNIT == 8 +typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI))); +typedef unsigned gcov_position_t __attribute__ ((mode (SI))); +#if LONG_LONG_TYPE_SIZE > 32 +typedef signed gcov_type __attribute__ ((mode (DI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI))); +#else +typedef signed gcov_type __attribute__ ((mode (SI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI))); +#endif +#else +#if BITS_PER_UNIT == 16 +typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI))); +typedef unsigned gcov_position_t __attribute__ ((mode (HI))); +#if LONG_LONG_TYPE_SIZE > 32 +typedef signed gcov_type __attribute__ ((mode (SI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI))); +#else +typedef signed gcov_type __attribute__ ((mode (HI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI))); +#endif +#else +typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI))); +typedef unsigned gcov_position_t __attribute__ ((mode (QI))); +#if LONG_LONG_TYPE_SIZE > 32 +typedef signed gcov_type __attribute__ ((mode (HI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI))); +#else +typedef signed gcov_type __attribute__ ((mode (QI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI))); +#endif +#endif +#endif + +static_assert(sizeof(gcov_type) == 8, "gcov_type isn't 64 bits for some reason"); + +#if __GNUC__ == 4 && __GNUC_MINOR__ == 9 +#define GCOV_COUNTERS 9 +#else +#error We dont support this compiler yet +#endif + +/* Structures embedded in coveraged program. The structures generated + by write_profile must match these. */ + +/* Information about counters for a single function. */ +struct gcov_ctr_info +{ + gcov_unsigned_t num; /* number of counters. */ + gcov_type *values; /* their values. */ +}; + +/* 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 */ + gcov_unsigned_t ident; /* unique ident of function */ + gcov_unsigned_t lineno_checksum; /* function lineno_checksum */ + gcov_unsigned_t cfg_checksum; /* function cfg checksum */ + struct gcov_ctr_info ctrs[0]; /* instrumented counters */ +}; + +/* Type of function used to merge counters. */ +typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t); + +/* Information about a single object file. */ struct gcov_info { - uint32_t version; - uint32_t unused0; - gcov_info* next; - uint32_t timestamp; - uint32_t unused1; - char* filename; - uint32_t n_functions; - uint32_t unused2; - void* functions; - uint32_t counter_mask; - uint32_t unused3; - uint32_t n_counters; - uint32_t unused4; - uint64_t* counters; -} PACKED; + gcov_unsigned_t version;/* expected version number */ + struct gcov_info *next; /* link to next, used by libgcov */ + + gcov_unsigned_t stamp; /* uniquifying time stamp */ + const char *filename; /* output file name */ + + gcov_merge_fn merge[GCOV_COUNTERS]; /* merge functions (null for + unused) */ + + unsigned n_functions; /* number of functions */ + const struct gcov_fn_info *const *functions; /* pointer to pointers + to function information */ +}; // Preprocessor magic to create a variable name based off the module name. // GCOV_INFO_OBJ() will create a post-processed name like @@ -90,6 +179,9 @@ struct gcov_info #define __GCOV_INFO_OBJ(X,Y) X ## Y #define _GCOV_INFO_OBJ(X,Y) __GCOV_INFO_OBJ(X,Y) #define GCOV_INFO_OBJ() _GCOV_INFO_OBJ(__GCOV_PREFIX, _gcov_info_head) +#define GCOV_INFO_MAGIC() _GCOV_INFO_OBJ(__GCOV_PREFIX, _gcov_info_magic) + +uint32_t GCOV_INFO_MAGIC() = 0xbeefb055; /** Pointer to the beginning of the gcov_info chain for this module. */ gcov_info* GCOV_INFO_OBJ() = NULL; @@ -121,7 +213,7 @@ void __gcov_init(gcov_info* i_info) // #ifdef __HOSTBOOT_MODULE // Forward declaration of __gcov_module_copychain for modules. -extern "C" void __gcov_module_copychain(gcov_info* chain); +extern "C" void __gcov_module_copychain(gcov_info** chain); /** Function called by module unloading to move the module's gcov_info * instances to the global chain. @@ -129,7 +221,7 @@ extern "C" void __gcov_module_copychain(gcov_info* chain); extern "C" void __gcov_module_unload(void* unused) { - __gcov_module_copychain(GCOV_INFO_OBJ()); + __gcov_module_copychain(&GCOV_INFO_OBJ()); } // Register __gcov_module_unload with __cxa_atexit. extern void* __dso_handle; @@ -137,27 +229,93 @@ extern "C" int __cxa_atexit(void(*)(void*),void*,void*); int __unused_gcov_cxa_register = __cxa_atexit(&__gcov_module_unload, NULL, __dso_handle); #else + +// This is set to 1 for now because it reduces memory pressure, but it +// won't work on hardware. See the comment below about +// MAGIC_GCOV_MODULE_UNLOAD. +#define HOSTBOOT_GCOV_EAGER_DATA_EXTRACTION 1 + /** Function called by a module being unloaded (via __gcov_module_unload) to * copy the module's gcov_info chain into the base gcov_info chain. */ extern "C" -void __gcov_module_copychain(gcov_info* chain) +void __gcov_module_copychain(gcov_info** const chain_ptr) { + gcov_info* chain = *chain_ptr; + +#if HOSTBOOT_GCOV_EAGER_DATA_EXTRACTION + asm volatile("mr %%r3, %0" + : + : "r" (chain) + : "%r3"); + + /* This magic instruction will cause simics to read the gcov_info + * pointer in r3 and use it to immediately extract this unloading + * module's data. We do this to reduce the max simultaneous memory + * pressure, otherwise we run out of memory having to preserve and + * store all the gcov info for all unloaded modules until the end + * of the run. This only works in simics, but the alternative + * won't work on hardware until we find other ways to reduce our + * memory footprint. */ + + MAGIC_INSTRUCTION(MAGIC_GCOV_MODULE_UNLOAD); +#else while(chain != NULL) { // Copy old info. - gcov_info* new_info = new gcov_info(); - memcpy(new_info, chain, sizeof(gcov_info)); + gcov_info* const new_info = new gcov_info(); + + memcpy(new_info, chain, sizeof(*chain)); - // Copy old counters. - uint64_t* new_counters = new uint64_t[chain->n_counters](); - memcpy(new_counters, chain->counters, - chain->n_counters*sizeof(uint64_t)); - new_info->counters = new_counters; + char* const new_filename = strdup(chain->filename); + new_info->filename = new_filename; + + struct gcov_fn_info** const new_functions + = new struct gcov_fn_info*[new_info->n_functions]; + + new_info->functions = new_functions; + + unsigned int num_gcov_counters = 0; + + for (unsigned int i = 0; i < GCOV_COUNTERS; ++i) + { + if (new_info->merge[i]) { + ++num_gcov_counters; + } + } + + for (unsigned int i = 0; i < chain->n_functions; ++i) + { + // malloc(base structure size + trailing array size) + new_functions[i] = + ((struct gcov_fn_info*) + (new char[sizeof(*new_functions[i]) + + (sizeof(new_functions[i]->ctrs[0]) + * num_gcov_counters)])); + + struct gcov_fn_info* const new_info = new_functions[i]; + const struct gcov_fn_info* const old_info = chain->functions[i]; + + for (unsigned int j = 0; j < num_gcov_counters; ++j) + { + const gcov_unsigned_t num_values = old_info->ctrs[j].num; + + new_info->key = NULL; + new_info->ctrs[j].num = num_values; + new_info->ctrs[j].values = new gcov_type[num_values]; + + memcpy(new_info->ctrs[j].values, + old_info->ctrs[j].values, + sizeof(gcov_type) * num_values); + } + } // Atomically push new_info onto the core_gcov_info_head stack. do { + /* GCOV_INFO_OBJ() in this function is always + core_gcov_info_head because this function is only + defined when __HOSTBOOT_MODULE is not defined */ new_info->next = GCOV_INFO_OBJ(); } while (!__sync_bool_compare_and_swap(&GCOV_INFO_OBJ(), new_info->next, new_info)); @@ -165,6 +323,12 @@ void __gcov_module_copychain(gcov_info* chain) // Advance to next info in this modules chain. chain = chain->next; } +#endif // #if HOSTBOOT_GCOV_EAGER_DATA_EXTRACTION + + /* Then we set the module info pointer to NULL so that simics + * won't dump it twice. */ + + *chain_ptr = NULL; } #endif |