summaryrefslogtreecommitdiffstats
path: root/src/include/usr/gcov.h
blob: 975e755a9a686f6b36066339cfc22a837255857f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/include/usr/gcov.h $                                      */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* 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.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#ifndef __USR_GCOV_H
#define __USR_GCOV_H

/** @file gcov.h
 *  @brief Header file to generate gcov_info chain for each module.
 *
 *  Each gcov-instrumented module needs to have its own gcov_info chain
 *  and associated linking function.  Each .o file will have its own
 *  gcov_info object which is added to the chain when the module is loaded
 *  (static initializers) by calling the __gcov_init function.
 *
 *  We make the gcov_info chain unique per-module, instead of a single
 *  global chain, so that we don't have bad pointers if a module was
 *  loaded and then unloaded.
 */

#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <arch/ppc.H>

/** @struct gcov_info
 *  @brief Structure generated by gcc.  Do not use.
 *
 *  This structure is automatically generated and instances of it created by
 *  gcc when the --coverage compile option is used.  We don't need to
 *  manipulate this structure from code except:
 *      1) To fix up the chains as objects are added to the chain.
 *      2) To copy the gcov_info and counters into the base-chain when we
 *         unload a module.
 *
 *  The rest of this structure is parsed by the Gcov.pm debug tool.
 *
 *  Most of the items in here are used as uint32_t's by gcov but are still
 *  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
#elif __GNUC__ == 8 && __GNUC_MINOR__ <= 3
#define GCOV_COUNTERS 9
#else
#error GCOV implementation does not 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
{
    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
// 'foobar_gcov_info_head' or 'core_gcov_info_head'.
#ifdef __HOSTBOOT_MODULE
    #define __GCOV_PREFIX __HOSTBOOT_MODULE
    #define ___GCOV_STRINGIFY(X) #X
    #define __GCOV_STRINGIFY(X) ___GCOV_STRINGIFY(X)
    #define __GCOV_PREFIX_NAME __GCOV_STRINGIFY(__HOSTBOOT_MODULE)
#else
    #define __GCOV_PREFIX core
    #define __GCOV_PREFIX_NAME "core"
#endif

#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;

/** Function called by module loading to add the object gcov_info instance
 *  to the chain.
 */
extern "C"
void __gcov_init(gcov_info* i_info)
{
    // Atomically push i_info onto the gcov_info_head stack.
    do
    {
        i_info->next = GCOV_INFO_OBJ();
    } while (!__sync_bool_compare_and_swap(&GCOV_INFO_OBJ(),
                                           i_info->next, i_info));
}

// This ifdef has two pieces of code which are used in module unloading.
//
// In the modules themselves we have a function that is registered via atexit
// to call to copy the contents of their own gcov_info chain into the base
// gcov_info chain.  This is required because the module's memory is going
// away as it is unloaded.
//
// In the base code (non-modules) we have a single implementation of the
// code for actually doing a copy of the gcov_info chain into the base code's
// own chain.  This is kept in just the base code for space savings.
//
#ifdef __HOSTBOOT_MODULE
// Forward declaration of __gcov_module_copychain for modules.
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.
 */
extern "C"
void __gcov_module_unload(void* unused)
{
    __gcov_module_copychain(&GCOV_INFO_OBJ());
}
    // Register __gcov_module_unload with __cxa_atexit.
extern void* __dso_handle;
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** 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* const new_info = new gcov_info();

        memcpy(new_info, chain, sizeof(*chain));

        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));

        // 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

/** Unneeded function but must be defined to link.
 *
 *  This function appears to be typically used by libgcov.so when instrumented
 *  on a real linux-based system.  It can be used to merge counters across
 *  multiple runs or when a 'fork' occurs.  It doesn't appear that this
 *  function ever gets called for us but the unresolved symbol is added to
 *  the module (by gcc) so we've created a stub here to pass compile.
 */
extern "C"
void __gcov_merge_add()
{
    while(1);
}

/** Unneeded function but must be defined to link. */

extern "C"
void __gcov_exit()
{
    MAGIC_INSTRUCTION(MAGIC_BREAK);
    while (1);
}

#endif
OpenPOWER on IntegriCloud