/* * 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 * * Uses gcc-internal data definitions. * * Imported from Linux and modified for Xen by * Wei Liu */ #include #include "gcov.h" #if !(GCC_VERSION >= 30400 && GCC_VERSION < 40700) #error "Wrong version of GCC used to compile gcov" #endif #define GCOV_COUNTERS 5 static struct gcov_info *gcov_info_head; /** * struct gcov_fn_info - profiling meta data per function * @ident: object file-unique function identifier * @checksum: function checksum * @n_ctrs: number of values per counter type belonging to this function * * This data is generated by gcc during compilation and doesn't change * at run-time. */ struct gcov_fn_info { unsigned int ident; unsigned int checksum; unsigned int n_ctrs[0]; }; /** * struct gcov_ctr_info - profiling data per counter type * @num: number of counter values for this type * @values: array of counter values for this type * @merge: merge function for counter values of this type (unused) * * This data is generated by gcc during compilation and doesn't change * at run-time with the exception of the values array. */ struct gcov_ctr_info { unsigned int num; gcov_type *values; void (*merge)(gcov_type *, unsigned int); }; /** * struct gcov_info - profiling data per object file * @version: gcov version magic indicating the gcc version used for compilation * @next: list head for a singly-linked list * @stamp: time stamp * @filename: name of the associated gcov data file * @n_functions: number of instrumented functions * @functions: function data * @ctr_mask: mask specifying which counter types are active * @counts: counter data per counter type * * This data is generated by gcc during compilation and doesn't change * at run-time with the exception of the next pointer. */ 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]; }; /** * 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 { const struct gcov_info *info; int record; unsigned int function; unsigned int type; unsigned int count; int num_types; struct type_info type_info[GCOV_COUNTERS]; }; /* 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 static int counter_active(const struct gcov_info *info, unsigned int type) { return (1 << type) & info->ctr_mask; } static unsigned int num_counter_active(const 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_link(struct gcov_info *info) { info->next = gcov_info_head; gcov_info_head = info; } struct gcov_info *gcov_info_next(const struct gcov_info *info) { if ( !info ) return gcov_info_head; return info->next; } const char *gcov_info_filename(const struct gcov_info *info) { return info->filename; } 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)); } static size_t get_fn_size(const 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 = ROUNDUP(size, __alignof__(struct gcov_fn_info)); return size; } static struct gcov_fn_info *get_fn_info(const struct gcov_info *info, unsigned int fn) { return (struct gcov_fn_info *) ((char *) info->functions + fn * get_fn_size(info)); } 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_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. */ static 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; } /** * gcov_iter_write - write data to buffer * @iter: file iterator * @buf: buffer to write to, if it is NULL, nothing is written * @pos: position inside buffer to start writing * * Return number of bytes written into buffer. */ static size_t gcov_iter_write(struct gcov_iterator *iter, char *buf, size_t pos) { size_t ret = 0; switch ( iter->record ) { case RECORD_FILE_MAGIC: ret = gcov_store_uint32(buf, pos, GCOV_DATA_MAGIC); break; case RECORD_GCOV_VERSION: ret = gcov_store_uint32(buf, pos, iter->info->version); break; case RECORD_TIME_STAMP: ret = gcov_store_uint32(buf, pos, iter->info->stamp); break; case RECORD_FUNCTION_TAG: ret = gcov_store_uint32(buf, pos, GCOV_TAG_FUNCTION); break; case RECORD_FUNCTON_TAG_LEN: ret = gcov_store_uint32(buf, pos, 2); break; case RECORD_FUNCTION_IDENT: ret = gcov_store_uint32(buf, pos, get_func(iter)->ident); break; case RECORD_FUNCTION_CHECK: ret = gcov_store_uint32(buf, pos, get_func(iter)->checksum); break; case RECORD_COUNT_TAG: ret = gcov_store_uint32(buf, pos, GCOV_TAG_FOR_COUNTER(get_type(iter)->ctr_type)); break; case RECORD_COUNT_LEN: ret = gcov_store_uint32(buf, pos, get_func(iter)->n_ctrs[iter->type] * 2); break; case RECORD_COUNT: ret = gcov_store_uint64(buf, pos, iter->info->counts[iter->type]. values[iter->count + get_type(iter)->offset]); break; } return ret; } /* If buffer is NULL, no data is written. */ size_t gcov_info_to_gcda(char *buffer, const struct gcov_info *info) { struct gcov_iterator iter = { .info = info }; unsigned int i; size_t pos = 0; for ( i = 0; i < GCOV_COUNTERS; i++ ) { if ( counter_active(info, i) ) { iter.type_info[iter.num_types].ctr_type = i; iter.type_info[iter.num_types].offset = 0; iter.num_types++; } } do { pos += gcov_iter_write(&iter, buffer, pos); } while ( gcov_iter_next(&iter) == 0 ); return pos; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */