123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- /*
- * This code provides functions to handle gcc's profiling data format
- * introduced with gcc 3.4. Future versions of gcc may change the gcov
- * format (as happened before), so all format-specific information needs
- * to be kept modular and easily exchangeable.
- *
- * This file is based on gcc-internal definitions. Functions and data
- * structures are defined to be compatible with gcc counterparts.
- * For a better understanding, refer to gcc source: gcc/gcov-io.h.
- *
- * Copyright IBM Corp. 2009
- * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
- *
- * Uses gcc-internal data definitions.
- */
- #include <linux/errno.h>
- #include <linux/slab.h>
- #include <linux/string.h>
- #include <linux/seq_file.h>
- #include <linux/vmalloc.h>
- #include "gcov.h"
- /* Symbolic links to be created for each profiling data file. */
- const struct gcov_link gcov_link[] = {
- { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
- { 0, NULL},
- };
- /*
- * Determine whether a counter is active. Based on gcc magic. Doesn't change
- * at run-time.
- */
- static int counter_active(struct gcov_info *info, unsigned int type)
- {
- return (1 << type) & info->ctr_mask;
- }
- /* Determine number of active counters. Based on gcc magic. */
- static unsigned int num_counter_active(struct gcov_info *info)
- {
- unsigned int i;
- unsigned int result = 0;
- for (i = 0; i < GCOV_COUNTERS; i++) {
- if (counter_active(info, i))
- result++;
- }
- return result;
- }
- /**
- * gcov_info_reset - reset profiling data to zero
- * @info: profiling data set
- */
- void gcov_info_reset(struct gcov_info *info)
- {
- unsigned int active = num_counter_active(info);
- unsigned int i;
- for (i = 0; i < active; i++) {
- memset(info->counts[i].values, 0,
- info->counts[i].num * sizeof(gcov_type));
- }
- }
- /**
- * gcov_info_is_compatible - check if profiling data can be added
- * @info1: first profiling data set
- * @info2: second profiling data set
- *
- * Returns non-zero if profiling data can be added, zero otherwise.
- */
- int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
- {
- return (info1->stamp == info2->stamp);
- }
- /**
- * gcov_info_add - add up profiling data
- * @dest: profiling data set to which data is added
- * @source: profiling data set which is added
- *
- * Adds profiling counts of @source to @dest.
- */
- void gcov_info_add(struct gcov_info *dest, struct gcov_info *source)
- {
- unsigned int i;
- unsigned int j;
- for (i = 0; i < num_counter_active(dest); i++) {
- for (j = 0; j < dest->counts[i].num; j++) {
- dest->counts[i].values[j] +=
- source->counts[i].values[j];
- }
- }
- }
- /* Get size of function info entry. Based on gcc magic. */
- static size_t get_fn_size(struct gcov_info *info)
- {
- size_t size;
- size = sizeof(struct gcov_fn_info) + num_counter_active(info) *
- sizeof(unsigned int);
- if (__alignof__(struct gcov_fn_info) > sizeof(unsigned int))
- size = ALIGN(size, __alignof__(struct gcov_fn_info));
- return size;
- }
- /* Get address of function info entry. Based on gcc magic. */
- static struct gcov_fn_info *get_fn_info(struct gcov_info *info, unsigned int fn)
- {
- return (struct gcov_fn_info *)
- ((char *) info->functions + fn * get_fn_size(info));
- }
- /**
- * gcov_info_dup - duplicate profiling data set
- * @info: profiling data set to duplicate
- *
- * Return newly allocated duplicate on success, %NULL on error.
- */
- struct gcov_info *gcov_info_dup(struct gcov_info *info)
- {
- struct gcov_info *dup;
- unsigned int i;
- unsigned int active;
- /* Duplicate gcov_info. */
- active = num_counter_active(info);
- dup = kzalloc(sizeof(struct gcov_info) +
- sizeof(struct gcov_ctr_info) * active, GFP_KERNEL);
- if (!dup)
- return NULL;
- dup->version = info->version;
- dup->stamp = info->stamp;
- dup->n_functions = info->n_functions;
- dup->ctr_mask = info->ctr_mask;
- /* Duplicate filename. */
- dup->filename = kstrdup(info->filename, GFP_KERNEL);
- if (!dup->filename)
- goto err_free;
- /* Duplicate table of functions. */
- dup->functions = kmemdup(info->functions, info->n_functions *
- get_fn_size(info), GFP_KERNEL);
- if (!dup->functions)
- goto err_free;
- /* Duplicate counter arrays. */
- for (i = 0; i < active ; i++) {
- struct gcov_ctr_info *ctr = &info->counts[i];
- size_t size = ctr->num * sizeof(gcov_type);
- dup->counts[i].num = ctr->num;
- dup->counts[i].merge = ctr->merge;
- dup->counts[i].values = vmalloc(size);
- if (!dup->counts[i].values)
- goto err_free;
- memcpy(dup->counts[i].values, ctr->values, size);
- }
- return dup;
- err_free:
- gcov_info_free(dup);
- return NULL;
- }
- /**
- * gcov_info_free - release memory for profiling data set duplicate
- * @info: profiling data set duplicate to free
- */
- void gcov_info_free(struct gcov_info *info)
- {
- unsigned int active = num_counter_active(info);
- unsigned int i;
- for (i = 0; i < active ; i++)
- vfree(info->counts[i].values);
- kfree(info->functions);
- kfree(info->filename);
- kfree(info);
- }
- /**
- * struct type_info - iterator helper array
- * @ctr_type: counter type
- * @offset: index of the first value of the current function for this type
- *
- * This array is needed to convert the in-memory data format into the in-file
- * data format:
- *
- * In-memory:
- * for each counter type
- * for each function
- * values
- *
- * In-file:
- * for each function
- * for each counter type
- * values
- *
- * See gcc source gcc/gcov-io.h for more information on data organization.
- */
- struct type_info {
- int ctr_type;
- unsigned int offset;
- };
- /**
- * struct gcov_iterator - specifies current file position in logical records
- * @info: associated profiling data
- * @record: record type
- * @function: function number
- * @type: counter type
- * @count: index into values array
- * @num_types: number of counter types
- * @type_info: helper array to get values-array offset for current function
- */
- struct gcov_iterator {
- struct gcov_info *info;
- int record;
- unsigned int function;
- unsigned int type;
- unsigned int count;
- int num_types;
- struct type_info type_info[0];
- };
- static struct gcov_fn_info *get_func(struct gcov_iterator *iter)
- {
- return get_fn_info(iter->info, iter->function);
- }
- static struct type_info *get_type(struct gcov_iterator *iter)
- {
- return &iter->type_info[iter->type];
- }
- /**
- * gcov_iter_new - allocate and initialize profiling data iterator
- * @info: profiling data set to be iterated
- *
- * Return file iterator on success, %NULL otherwise.
- */
- struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
- {
- struct gcov_iterator *iter;
- iter = kzalloc(sizeof(struct gcov_iterator) +
- num_counter_active(info) * sizeof(struct type_info),
- GFP_KERNEL);
- if (iter)
- iter->info = info;
- return iter;
- }
- /**
- * gcov_iter_free - release memory for iterator
- * @iter: file iterator to free
- */
- void gcov_iter_free(struct gcov_iterator *iter)
- {
- kfree(iter);
- }
- /**
- * gcov_iter_get_info - return profiling data set for given file iterator
- * @iter: file iterator
- */
- struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
- {
- return iter->info;
- }
- /**
- * gcov_iter_start - reset file iterator to starting position
- * @iter: file iterator
- */
- void gcov_iter_start(struct gcov_iterator *iter)
- {
- int i;
- iter->record = 0;
- iter->function = 0;
- iter->type = 0;
- iter->count = 0;
- iter->num_types = 0;
- for (i = 0; i < GCOV_COUNTERS; i++) {
- if (counter_active(iter->info, i)) {
- iter->type_info[iter->num_types].ctr_type = i;
- iter->type_info[iter->num_types++].offset = 0;
- }
- }
- }
- /* Mapping of logical record number to actual file content. */
- #define RECORD_FILE_MAGIC 0
- #define RECORD_GCOV_VERSION 1
- #define RECORD_TIME_STAMP 2
- #define RECORD_FUNCTION_TAG 3
- #define RECORD_FUNCTON_TAG_LEN 4
- #define RECORD_FUNCTION_IDENT 5
- #define RECORD_FUNCTION_CHECK 6
- #define RECORD_COUNT_TAG 7
- #define RECORD_COUNT_LEN 8
- #define RECORD_COUNT 9
- /**
- * gcov_iter_next - advance file iterator to next logical record
- * @iter: file iterator
- *
- * Return zero if new position is valid, non-zero if iterator has reached end.
- */
- int gcov_iter_next(struct gcov_iterator *iter)
- {
- switch (iter->record) {
- case RECORD_FILE_MAGIC:
- case RECORD_GCOV_VERSION:
- case RECORD_FUNCTION_TAG:
- case RECORD_FUNCTON_TAG_LEN:
- case RECORD_FUNCTION_IDENT:
- case RECORD_COUNT_TAG:
- /* Advance to next record */
- iter->record++;
- break;
- case RECORD_COUNT:
- /* Advance to next count */
- iter->count++;
- /* fall through */
- case RECORD_COUNT_LEN:
- if (iter->count < get_func(iter)->n_ctrs[iter->type]) {
- iter->record = 9;
- break;
- }
- /* Advance to next counter type */
- get_type(iter)->offset += iter->count;
- iter->count = 0;
- iter->type++;
- /* fall through */
- case RECORD_FUNCTION_CHECK:
- if (iter->type < iter->num_types) {
- iter->record = 7;
- break;
- }
- /* Advance to next function */
- iter->type = 0;
- iter->function++;
- /* fall through */
- case RECORD_TIME_STAMP:
- if (iter->function < iter->info->n_functions)
- iter->record = 3;
- else
- iter->record = -1;
- break;
- }
- /* Check for EOF. */
- if (iter->record == -1)
- return -EINVAL;
- else
- return 0;
- }
- /**
- * seq_write_gcov_u32 - write 32 bit number in gcov format to seq_file
- * @seq: seq_file handle
- * @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.
- */
- static int seq_write_gcov_u32(struct seq_file *seq, u32 v)
- {
- return seq_write(seq, &v, sizeof(v));
- }
- /**
- * seq_write_gcov_u64 - write 64 bit number in gcov format to seq_file
- * @seq: seq_file handle
- * @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.
- */
- static int seq_write_gcov_u64(struct seq_file *seq, u64 v)
- {
- u32 data[2];
- data[0] = (v & 0xffffffffUL);
- data[1] = (v >> 32);
- return seq_write(seq, data, sizeof(data));
- }
- /**
- * gcov_iter_write - write data for current pos to seq_file
- * @iter: file iterator
- * @seq: seq_file handle
- *
- * Return zero on success, non-zero otherwise.
- */
- int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
- {
- int rc = -EINVAL;
- switch (iter->record) {
- case RECORD_FILE_MAGIC:
- rc = seq_write_gcov_u32(seq, GCOV_DATA_MAGIC);
- break;
- case RECORD_GCOV_VERSION:
- rc = seq_write_gcov_u32(seq, iter->info->version);
- break;
- case RECORD_TIME_STAMP:
- rc = seq_write_gcov_u32(seq, iter->info->stamp);
- break;
- case RECORD_FUNCTION_TAG:
- rc = seq_write_gcov_u32(seq, GCOV_TAG_FUNCTION);
- break;
- case RECORD_FUNCTON_TAG_LEN:
- rc = seq_write_gcov_u32(seq, 2);
- break;
- case RECORD_FUNCTION_IDENT:
- rc = seq_write_gcov_u32(seq, get_func(iter)->ident);
- break;
- case RECORD_FUNCTION_CHECK:
- rc = seq_write_gcov_u32(seq, get_func(iter)->checksum);
- break;
- case RECORD_COUNT_TAG:
- rc = seq_write_gcov_u32(seq,
- GCOV_TAG_FOR_COUNTER(get_type(iter)->ctr_type));
- break;
- case RECORD_COUNT_LEN:
- rc = seq_write_gcov_u32(seq,
- get_func(iter)->n_ctrs[iter->type] * 2);
- break;
- case RECORD_COUNT:
- rc = seq_write_gcov_u64(seq,
- iter->info->counts[iter->type].
- values[iter->count + get_type(iter)->offset]);
- break;
- }
- return rc;
- }
|