123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- #include <linux/errno.h>
- #include <linux/slab.h>
- #include <linux/string.h>
- #include <linux/seq_file.h>
- #include <linux/vmalloc.h>
- #include "gcov.h"
- #define GCOV_COUNTERS 5
- static struct gcov_info *gcov_info_head;
- struct gcov_fn_info {
- unsigned int ident;
- unsigned int checksum;
- unsigned int n_ctrs[0];
- };
- struct gcov_ctr_info {
- unsigned int num;
- gcov_type *values;
- void (*merge)(gcov_type *, unsigned int);
- };
- struct gcov_info {
- unsigned int version;
- struct gcov_info *next;
- unsigned int stamp;
- const char *filename;
- unsigned int n_functions;
- const struct gcov_fn_info *functions;
- unsigned int ctr_mask;
- struct gcov_ctr_info counts[0];
- };
- const char *gcov_info_filename(struct gcov_info *info)
- {
- return info->filename;
- }
- unsigned int gcov_info_version(struct gcov_info *info)
- {
- return info->version;
- }
- struct gcov_info *gcov_info_next(struct gcov_info *info)
- {
- if (!info)
- return gcov_info_head;
- return info->next;
- }
- void gcov_info_link(struct gcov_info *info)
- {
- info->next = gcov_info_head;
- gcov_info_head = info;
- }
- void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
- {
- if (prev)
- prev->next = info->next;
- else
- gcov_info_head = info->next;
- }
- const struct gcov_link gcov_link[] = {
- { OBJ_TREE, "gcno" },
- { 0, NULL},
- };
- static int counter_active(struct gcov_info *info, unsigned int type)
- {
- return (1 << type) & info->ctr_mask;
- }
- 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;
- }
- 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));
- }
- }
- int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
- {
- return (info1->stamp == info2->stamp);
- }
- 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];
- }
- }
- }
- 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;
- }
- 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));
- }
- struct gcov_info *gcov_info_dup(struct gcov_info *info)
- {
- struct gcov_info *dup;
- unsigned int i;
- unsigned int active;
-
- 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;
-
- dup->filename = kstrdup(info->filename, GFP_KERNEL);
- if (!dup->filename)
- goto err_free;
-
- dup->functions = kmemdup(info->functions, info->n_functions *
- get_fn_size(info), GFP_KERNEL);
- if (!dup->functions)
- goto err_free;
-
- 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;
- }
- 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 {
- int ctr_type;
- unsigned int offset;
- };
- 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];
- }
- 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;
- }
- void gcov_iter_free(struct gcov_iterator *iter)
- {
- kfree(iter);
- }
- struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
- {
- return iter->info;
- }
- 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;
- }
- }
- }
- #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
- 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:
-
- iter->record++;
- break;
- case RECORD_COUNT:
-
- iter->count++;
-
- case RECORD_COUNT_LEN:
- if (iter->count < get_func(iter)->n_ctrs[iter->type]) {
- iter->record = 9;
- break;
- }
-
- get_type(iter)->offset += iter->count;
- iter->count = 0;
- iter->type++;
-
- case RECORD_FUNCTION_CHECK:
- if (iter->type < iter->num_types) {
- iter->record = 7;
- break;
- }
-
- iter->type = 0;
- iter->function++;
-
- case RECORD_TIME_STAMP:
- if (iter->function < iter->info->n_functions)
- iter->record = 3;
- else
- iter->record = -1;
- break;
- }
-
- if (iter->record == -1)
- return -EINVAL;
- else
- return 0;
- }
- static int seq_write_gcov_u32(struct seq_file *seq, u32 v)
- {
- return seq_write(seq, &v, sizeof(v));
- }
- 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));
- }
- 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;
- }
|