linux/kernel/gcov/gcc_3_4.c
<<
>>
Prefs
   1/*
   2 *  This code provides functions to handle gcc's profiling data format
   3 *  introduced with gcc 3.4. Future versions of gcc may change the gcov
   4 *  format (as happened before), so all format-specific information needs
   5 *  to be kept modular and easily exchangeable.
   6 *
   7 *  This file is based on gcc-internal definitions. Functions and data
   8 *  structures are defined to be compatible with gcc counterparts.
   9 *  For a better understanding, refer to gcc source: gcc/gcov-io.h.
  10 *
  11 *    Copyright IBM Corp. 2009
  12 *    Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
  13 *
  14 *    Uses gcc-internal data definitions.
  15 */
  16
  17#include <linux/errno.h>
  18#include <linux/slab.h>
  19#include <linux/string.h>
  20#include <linux/seq_file.h>
  21#include <linux/vmalloc.h>
  22#include "gcov.h"
  23
  24/* Symbolic links to be created for each profiling data file. */
  25const struct gcov_link gcov_link[] = {
  26        { OBJ_TREE, "gcno" },   /* Link to .gcno file in $(objtree). */
  27        { 0, NULL},
  28};
  29
  30/*
  31 * Determine whether a counter is active. Based on gcc magic. Doesn't change
  32 * at run-time.
  33 */
  34static int counter_active(struct gcov_info *info, unsigned int type)
  35{
  36        return (1 << type) & info->ctr_mask;
  37}
  38
  39/* Determine number of active counters. Based on gcc magic. */
  40static unsigned int num_counter_active(struct gcov_info *info)
  41{
  42        unsigned int i;
  43        unsigned int result = 0;
  44
  45        for (i = 0; i < GCOV_COUNTERS; i++) {
  46                if (counter_active(info, i))
  47                        result++;
  48        }
  49        return result;
  50}
  51
  52/**
  53 * gcov_info_reset - reset profiling data to zero
  54 * @info: profiling data set
  55 */
  56void gcov_info_reset(struct gcov_info *info)
  57{
  58        unsigned int active = num_counter_active(info);
  59        unsigned int i;
  60
  61        for (i = 0; i < active; i++) {
  62                memset(info->counts[i].values, 0,
  63                       info->counts[i].num * sizeof(gcov_type));
  64        }
  65}
  66
  67/**
  68 * gcov_info_is_compatible - check if profiling data can be added
  69 * @info1: first profiling data set
  70 * @info2: second profiling data set
  71 *
  72 * Returns non-zero if profiling data can be added, zero otherwise.
  73 */
  74int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
  75{
  76        return (info1->stamp == info2->stamp);
  77}
  78
  79/**
  80 * gcov_info_add - add up profiling data
  81 * @dest: profiling data set to which data is added
  82 * @source: profiling data set which is added
  83 *
  84 * Adds profiling counts of @source to @dest.
  85 */
  86void gcov_info_add(struct gcov_info *dest, struct gcov_info *source)
  87{
  88        unsigned int i;
  89        unsigned int j;
  90
  91        for (i = 0; i < num_counter_active(dest); i++) {
  92                for (j = 0; j < dest->counts[i].num; j++) {
  93                        dest->counts[i].values[j] +=
  94                                source->counts[i].values[j];
  95                }
  96        }
  97}
  98
  99/* Get size of function info entry. Based on gcc magic. */
 100static size_t get_fn_size(struct gcov_info *info)
 101{
 102        size_t size;
 103
 104        size = sizeof(struct gcov_fn_info) + num_counter_active(info) *
 105               sizeof(unsigned int);
 106        if (__alignof__(struct gcov_fn_info) > sizeof(unsigned int))
 107                size = ALIGN(size, __alignof__(struct gcov_fn_info));
 108        return size;
 109}
 110
 111/* Get address of function info entry. Based on gcc magic. */
 112static struct gcov_fn_info *get_fn_info(struct gcov_info *info, unsigned int fn)
 113{
 114        return (struct gcov_fn_info *)
 115                ((char *) info->functions + fn * get_fn_size(info));
 116}
 117
 118/**
 119 * gcov_info_dup - duplicate profiling data set
 120 * @info: profiling data set to duplicate
 121 *
 122 * Return newly allocated duplicate on success, %NULL on error.
 123 */
 124struct gcov_info *gcov_info_dup(struct gcov_info *info)
 125{
 126        struct gcov_info *dup;
 127        unsigned int i;
 128        unsigned int active;
 129
 130        /* Duplicate gcov_info. */
 131        active = num_counter_active(info);
 132        dup = kzalloc(sizeof(struct gcov_info) +
 133                      sizeof(struct gcov_ctr_info) * active, GFP_KERNEL);
 134        if (!dup)
 135                return NULL;
 136        dup->version            = info->version;
 137        dup->stamp              = info->stamp;
 138        dup->n_functions        = info->n_functions;
 139        dup->ctr_mask           = info->ctr_mask;
 140        /* Duplicate filename. */
 141        dup->filename           = kstrdup(info->filename, GFP_KERNEL);
 142        if (!dup->filename)
 143                goto err_free;
 144        /* Duplicate table of functions. */
 145        dup->functions = kmemdup(info->functions, info->n_functions *
 146                                 get_fn_size(info), GFP_KERNEL);
 147        if (!dup->functions)
 148                goto err_free;
 149        /* Duplicate counter arrays. */
 150        for (i = 0; i < active ; i++) {
 151                struct gcov_ctr_info *ctr = &info->counts[i];
 152                size_t size = ctr->num * sizeof(gcov_type);
 153
 154                dup->counts[i].num = ctr->num;
 155                dup->counts[i].merge = ctr->merge;
 156                dup->counts[i].values = vmalloc(size);
 157                if (!dup->counts[i].values)
 158                        goto err_free;
 159                memcpy(dup->counts[i].values, ctr->values, size);
 160        }
 161        return dup;
 162
 163err_free:
 164        gcov_info_free(dup);
 165        return NULL;
 166}
 167
 168/**
 169 * gcov_info_free - release memory for profiling data set duplicate
 170 * @info: profiling data set duplicate to free
 171 */
 172void gcov_info_free(struct gcov_info *info)
 173{
 174        unsigned int active = num_counter_active(info);
 175        unsigned int i;
 176
 177        for (i = 0; i < active ; i++)
 178                vfree(info->counts[i].values);
 179        kfree(info->functions);
 180        kfree(info->filename);
 181        kfree(info);
 182}
 183
 184/**
 185 * struct type_info - iterator helper array
 186 * @ctr_type: counter type
 187 * @offset: index of the first value of the current function for this type
 188 *
 189 * This array is needed to convert the in-memory data format into the in-file
 190 * data format:
 191 *
 192 * In-memory:
 193 *   for each counter type
 194 *     for each function
 195 *       values
 196 *
 197 * In-file:
 198 *   for each function
 199 *     for each counter type
 200 *       values
 201 *
 202 * See gcc source gcc/gcov-io.h for more information on data organization.
 203 */
 204struct type_info {
 205        int ctr_type;
 206        unsigned int offset;
 207};
 208
 209/**
 210 * struct gcov_iterator - specifies current file position in logical records
 211 * @info: associated profiling data
 212 * @record: record type
 213 * @function: function number
 214 * @type: counter type
 215 * @count: index into values array
 216 * @num_types: number of counter types
 217 * @type_info: helper array to get values-array offset for current function
 218 */
 219struct gcov_iterator {
 220        struct gcov_info *info;
 221
 222        int record;
 223        unsigned int function;
 224        unsigned int type;
 225        unsigned int count;
 226
 227        int num_types;
 228        struct type_info type_info[0];
 229};
 230
 231static struct gcov_fn_info *get_func(struct gcov_iterator *iter)
 232{
 233        return get_fn_info(iter->info, iter->function);
 234}
 235
 236static struct type_info *get_type(struct gcov_iterator *iter)
 237{
 238        return &iter->type_info[iter->type];
 239}
 240
 241/**
 242 * gcov_iter_new - allocate and initialize profiling data iterator
 243 * @info: profiling data set to be iterated
 244 *
 245 * Return file iterator on success, %NULL otherwise.
 246 */
 247struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
 248{
 249        struct gcov_iterator *iter;
 250
 251        iter = kzalloc(sizeof(struct gcov_iterator) +
 252                       num_counter_active(info) * sizeof(struct type_info),
 253                       GFP_KERNEL);
 254        if (iter)
 255                iter->info = info;
 256
 257        return iter;
 258}
 259
 260/**
 261 * gcov_iter_free - release memory for iterator
 262 * @iter: file iterator to free
 263 */
 264void gcov_iter_free(struct gcov_iterator *iter)
 265{
 266        kfree(iter);
 267}
 268
 269/**
 270 * gcov_iter_get_info - return profiling data set for given file iterator
 271 * @iter: file iterator
 272 */
 273struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
 274{
 275        return iter->info;
 276}
 277
 278/**
 279 * gcov_iter_start - reset file iterator to starting position
 280 * @iter: file iterator
 281 */
 282void gcov_iter_start(struct gcov_iterator *iter)
 283{
 284        int i;
 285
 286        iter->record = 0;
 287        iter->function = 0;
 288        iter->type = 0;
 289        iter->count = 0;
 290        iter->num_types = 0;
 291        for (i = 0; i < GCOV_COUNTERS; i++) {
 292                if (counter_active(iter->info, i)) {
 293                        iter->type_info[iter->num_types].ctr_type = i;
 294                        iter->type_info[iter->num_types++].offset = 0;
 295                }
 296        }
 297}
 298
 299/* Mapping of logical record number to actual file content. */
 300#define RECORD_FILE_MAGIC       0
 301#define RECORD_GCOV_VERSION     1
 302#define RECORD_TIME_STAMP       2
 303#define RECORD_FUNCTION_TAG     3
 304#define RECORD_FUNCTON_TAG_LEN  4
 305#define RECORD_FUNCTION_IDENT   5
 306#define RECORD_FUNCTION_CHECK   6
 307#define RECORD_COUNT_TAG        7
 308#define RECORD_COUNT_LEN        8
 309#define RECORD_COUNT            9
 310
 311/**
 312 * gcov_iter_next - advance file iterator to next logical record
 313 * @iter: file iterator
 314 *
 315 * Return zero if new position is valid, non-zero if iterator has reached end.
 316 */
 317int gcov_iter_next(struct gcov_iterator *iter)
 318{
 319        switch (iter->record) {
 320        case RECORD_FILE_MAGIC:
 321        case RECORD_GCOV_VERSION:
 322        case RECORD_FUNCTION_TAG:
 323        case RECORD_FUNCTON_TAG_LEN:
 324        case RECORD_FUNCTION_IDENT:
 325        case RECORD_COUNT_TAG:
 326                /* Advance to next record */
 327                iter->record++;
 328                break;
 329        case RECORD_COUNT:
 330                /* Advance to next count */
 331                iter->count++;
 332                /* fall through */
 333        case RECORD_COUNT_LEN:
 334                if (iter->count < get_func(iter)->n_ctrs[iter->type]) {
 335                        iter->record = 9;
 336                        break;
 337                }
 338                /* Advance to next counter type */
 339                get_type(iter)->offset += iter->count;
 340                iter->count = 0;
 341                iter->type++;
 342                /* fall through */
 343        case RECORD_FUNCTION_CHECK:
 344                if (iter->type < iter->num_types) {
 345                        iter->record = 7;
 346                        break;
 347                }
 348                /* Advance to next function */
 349                iter->type = 0;
 350                iter->function++;
 351                /* fall through */
 352        case RECORD_TIME_STAMP:
 353                if (iter->function < iter->info->n_functions)
 354                        iter->record = 3;
 355                else
 356                        iter->record = -1;
 357                break;
 358        }
 359        /* Check for EOF. */
 360        if (iter->record == -1)
 361                return -EINVAL;
 362        else
 363                return 0;
 364}
 365
 366/**
 367 * seq_write_gcov_u32 - write 32 bit number in gcov format to seq_file
 368 * @seq: seq_file handle
 369 * @v: value to be stored
 370 *
 371 * Number format defined by gcc: numbers are recorded in the 32 bit
 372 * unsigned binary form of the endianness of the machine generating the
 373 * file.
 374 */
 375static int seq_write_gcov_u32(struct seq_file *seq, u32 v)
 376{
 377        return seq_write(seq, &v, sizeof(v));
 378}
 379
 380/**
 381 * seq_write_gcov_u64 - write 64 bit number in gcov format to seq_file
 382 * @seq: seq_file handle
 383 * @v: value to be stored
 384 *
 385 * Number format defined by gcc: numbers are recorded in the 32 bit
 386 * unsigned binary form of the endianness of the machine generating the
 387 * file. 64 bit numbers are stored as two 32 bit numbers, the low part
 388 * first.
 389 */
 390static int seq_write_gcov_u64(struct seq_file *seq, u64 v)
 391{
 392        u32 data[2];
 393
 394        data[0] = (v & 0xffffffffUL);
 395        data[1] = (v >> 32);
 396        return seq_write(seq, data, sizeof(data));
 397}
 398
 399/**
 400 * gcov_iter_write - write data for current pos to seq_file
 401 * @iter: file iterator
 402 * @seq: seq_file handle
 403 *
 404 * Return zero on success, non-zero otherwise.
 405 */
 406int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
 407{
 408        int rc = -EINVAL;
 409
 410        switch (iter->record) {
 411        case RECORD_FILE_MAGIC:
 412                rc = seq_write_gcov_u32(seq, GCOV_DATA_MAGIC);
 413                break;
 414        case RECORD_GCOV_VERSION:
 415                rc = seq_write_gcov_u32(seq, iter->info->version);
 416                break;
 417        case RECORD_TIME_STAMP:
 418                rc = seq_write_gcov_u32(seq, iter->info->stamp);
 419                break;
 420        case RECORD_FUNCTION_TAG:
 421                rc = seq_write_gcov_u32(seq, GCOV_TAG_FUNCTION);
 422                break;
 423        case RECORD_FUNCTON_TAG_LEN:
 424                rc = seq_write_gcov_u32(seq, 2);
 425                break;
 426        case RECORD_FUNCTION_IDENT:
 427                rc = seq_write_gcov_u32(seq, get_func(iter)->ident);
 428                break;
 429        case RECORD_FUNCTION_CHECK:
 430                rc = seq_write_gcov_u32(seq, get_func(iter)->checksum);
 431                break;
 432        case RECORD_COUNT_TAG:
 433                rc = seq_write_gcov_u32(seq,
 434                        GCOV_TAG_FOR_COUNTER(get_type(iter)->ctr_type));
 435                break;
 436        case RECORD_COUNT_LEN:
 437                rc = seq_write_gcov_u32(seq,
 438                                get_func(iter)->n_ctrs[iter->type] * 2);
 439                break;
 440        case RECORD_COUNT:
 441                rc = seq_write_gcov_u64(seq,
 442                        iter->info->counts[iter->type].
 443                                values[iter->count + get_type(iter)->offset]);
 444                break;
 445        }
 446        return rc;
 447}
 448
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.