summaryrefslogtreecommitdiffstats
path: root/src/include/usr/gcov.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/usr/gcov.h')
-rw-r--r--src/include/usr/gcov.h216
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
OpenPOWER on IntegriCloud