diff options
Diffstat (limited to 'kernel/gcov/gcc_3_4.c')
| -rw-r--r-- | kernel/gcov/gcc_3_4.c | 447 | 
1 files changed, 447 insertions, 0 deletions
| diff --git a/kernel/gcov/gcc_3_4.c b/kernel/gcov/gcc_3_4.c new file mode 100644 index 000000000000..ae5bb4260033 --- /dev/null +++ b/kernel/gcov/gcc_3_4.c @@ -0,0 +1,447 @@ +/* + *  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 <[email protected]> + * + *    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; +} |