diff --git a/xen/Kconfig.debug b/xen/Kconfig.debug index ef863dc265..b19b3579c5 100644 --- a/xen/Kconfig.debug +++ b/xen/Kconfig.debug @@ -30,16 +30,45 @@ config FRAME_POINTER config GCOV bool "Gcov Support" - depends on BROKEN + depends on !LIVEPATCH ---help--- Enable gcov (a test coverage program in GCC) support. - Currently the data structure and hypercall interface are tied - to GCC 3.4 gcov format. You need to have a version of GCC - that is compatible with that format to make gcov work. - If unsure, say N here. +choice + prompt "Specify Gcov format" + depends on GCOV + default GCOV_FORMAT_5 + ---help--- + The gcov format is determined by gcc version. + +config GCOV_FORMAT_5 + bool "GCC 5 format" + ---help--- + Select this option to use the format specified in GCC 5. + Works in gcc version range [5, ...). + +config GCOV_FORMAT_4_9 + bool "GCC 4.9 format" + ---help--- + Select this option to use the format specified in GCC 4.9. + Works in gcc version range [4.9, 5). + +config GCOV_FORMAT_4_7 + bool "GCC 4.7 format" + ---help--- + Select this option to use the format specified in GCC 4.7. + Works in gcc version range [4.7, 4.9). + +config GCOV_FORMAT_3_4 + bool "GCC 3.4 format" + ---help--- + Select this option to use the format specified in GCC 3.4. + Works in gcc version range [3.4, 4.7). + +endchoice + config LOCK_PROFILE bool "Lock Profiling" ---help--- diff --git a/xen/common/gcov/Makefile b/xen/common/gcov/Makefile index e69de29bb2..6a304ab3e7 100644 --- a/xen/common/gcov/Makefile +++ b/xen/common/gcov/Makefile @@ -0,0 +1,5 @@ +obj-y += gcov_base.o gcov.o +obj-$(CONFIG_GCOV_FORMAT_3_4) += gcc_3_4.o +obj-$(CONFIG_GCOV_FORMAT_4_7) += gcc_4_7.o +obj-$(CONFIG_GCOV_FORMAT_4_9) += gcc_4_9.o +obj-$(CONFIG_GCOV_FORMAT_5) += gcc_5.o diff --git a/xen/common/gcov/gcc_3_4.c b/xen/common/gcov/gcc_3_4.c new file mode 100644 index 0000000000..3631f4bc25 --- /dev/null +++ b/xen/common/gcov/gcc_3_4.c @@ -0,0 +1,367 @@ +/* + * 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: + */ diff --git a/xen/common/gcov/gcc_4_7.c b/xen/common/gcov/gcc_4_7.c new file mode 100644 index 0000000000..25b4a8bcdc --- /dev/null +++ b/xen/common/gcov/gcc_4_7.c @@ -0,0 +1,204 @@ +/* + * This code provides functions to handle gcc's profiling data format + * introduced with gcc 4.7. + * + * This file is based heavily on gcc_3_4.c file. + * + * For a better understanding, refer to gcc source: + * gcc/gcov-io.h + * libgcc/libgcov.c + * + * Uses gcc-internal data definitions. + * + * Imported from Linux and modified for Xen by + * Wei Liu + */ + +#include + +#include "gcov.h" + +/* + * GCOV_COUNTERS will be defined if this file is included by other + * source files. + */ +#ifndef GCOV_COUNTERS +# if !(GCC_VERSION >= 40700 && GCC_VERSION < 40900) +# error "Wrong version of GCC used to compile gcov" +# endif +#define GCOV_COUNTERS 8 +#endif + +#define GCOV_TAG_FUNCTION_LENGTH 3 + +static struct gcov_info *gcov_info_head; + +/** + * struct gcov_ctr_info - information about counters for a single function + * @num: number of counter values for this type + * @values: array of counter values for this type + * + * 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; +}; + +/** + * struct gcov_fn_info - profiling meta data per function + * @key: comdat key + * @ident: unique ident of function + * @lineno_checksum: function lineo_checksum + * @cfg_checksum: function cfg checksum + * @ctrs: instrumented counters + * + * This data is generated by gcc during compilation and doesn't change + * at run-time. + * + * Information about a single function. This uses the trailing array + * idiom. The number of counters is determined from the merge pointer + * array in gcov_info. The key is used to detect which of a set of + * comdat functions was selected -- it points to the gcov_info object + * of the object file containing the selected comdat function. + */ +struct gcov_fn_info { + const struct gcov_info *key; + unsigned int ident; + unsigned int lineno_checksum; + unsigned int cfg_checksum; + struct gcov_ctr_info ctrs[0]; +}; + +/** + * 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: uniquifying time stamp + * @filename: name of the associated gcov data file + * @merge: merge functions (null for unused counter type) + * @n_functions: number of instrumented functions + * @functions: pointer to pointers to function information + * + * 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; + void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int); + unsigned int n_functions; + struct gcov_fn_info **functions; +}; + +static int counter_active(const struct gcov_info *info, unsigned int type) +{ + return info->merge[type] ? 1 : 0; +} + +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; +} + +void gcov_info_reset(struct gcov_info *info) +{ + struct gcov_ctr_info *ci_ptr; + unsigned int fi_idx; + unsigned int ct_idx; + + for ( fi_idx = 0; fi_idx < info->n_functions; fi_idx++ ) + { + ci_ptr = info->functions[fi_idx]->ctrs; + + for ( ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++ ) + { + if ( !counter_active(info, ct_idx) ) + continue; + + memset(ci_ptr->values, 0, sizeof(gcov_type) * ci_ptr->num); + ci_ptr++; + } + } +} + +const char *gcov_info_filename(const struct gcov_info *info) +{ + return info->filename; +} + + +/** + * gcov_info_to_gcda - convert profiling data set to gcda file format + * @buffer: the buffer to store file data or %NULL if no data should be stored + * @info: profiling data set to be converted + * + * Returns the number of bytes that were/would have been stored into the buffer. + */ +size_t gcov_info_to_gcda(char *buffer, const struct gcov_info *info) +{ + struct gcov_fn_info *fi_ptr; + struct gcov_ctr_info *ci_ptr; + unsigned int fi_idx; + unsigned int ct_idx; + unsigned int cv_idx; + size_t pos = 0; + + /* File header. */ + pos += gcov_store_uint32(buffer, pos, GCOV_DATA_MAGIC); + pos += gcov_store_uint32(buffer, pos, info->version); + pos += gcov_store_uint32(buffer, pos, info->stamp); + + for ( fi_idx = 0; fi_idx < info->n_functions; fi_idx++ ) + { + fi_ptr = info->functions[fi_idx]; + + /* Function record. */ + pos += gcov_store_uint32(buffer, pos, GCOV_TAG_FUNCTION); + pos += gcov_store_uint32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH); + pos += gcov_store_uint32(buffer, pos, fi_ptr->ident); + pos += gcov_store_uint32(buffer, pos, fi_ptr->lineno_checksum); + pos += gcov_store_uint32(buffer, pos, fi_ptr->cfg_checksum); + + ci_ptr = fi_ptr->ctrs; + + for ( ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++ ) + { + if (! counter_active(info, ct_idx) ) + continue; + + /* Counter record. */ + pos += gcov_store_uint32(buffer, pos, + GCOV_TAG_FOR_COUNTER(ct_idx)); + pos += gcov_store_uint32(buffer, pos, ci_ptr->num * 2); + + for ( cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++ ) + pos += gcov_store_uint64(buffer, pos, ci_ptr->values[cv_idx]); + + ci_ptr++; + } + } + + return pos; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/gcov/gcc_4_9.c b/xen/common/gcov/gcc_4_9.c new file mode 100644 index 0000000000..dcea961936 --- /dev/null +++ b/xen/common/gcov/gcc_4_9.c @@ -0,0 +1,33 @@ +/* + * This code provides functions to handle gcc's profiling data format + * introduced with gcc 4.7. + * + * For a better understanding, refer to gcc source: + * gcc/gcov-io.h + * libgcc/libgcov.c + * + * Uses gcc-internal data definitions. + * + * Imported from Linux and modified for Xen by + * Wei Liu + */ + +#include "gcov.h" + +#if !(GCC_VERSION >= 40900 && GCC_VERSION < 50000) +#error "Wrong version of GCC used to compile gcov" +#endif + +#define GCOV_COUNTERS 9 + +#include "gcc_4_7.c" + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/gcov/gcc_5.c b/xen/common/gcov/gcc_5.c new file mode 100644 index 0000000000..7541310320 --- /dev/null +++ b/xen/common/gcov/gcc_5.c @@ -0,0 +1,33 @@ +/* + * This code provides functions to handle gcc's profiling data format + * introduced with gcc 4.7. + * + * For a better understanding, refer to gcc source: + * gcc/gcov-io.h + * libgcc/libgcov.c + * + * Uses gcc-internal data definitions. + * + * Imported from Linux and modified for Xen by + * Wei Liu + */ + +#include "gcov.h" + +#if GCC_VERSION < 50000 +#error "Wrong version of GCC used to compile gcov" +#endif + +#define GCOV_COUNTERS 10 + +#include "gcc_4_7.c" + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/gcov/gcov.c b/xen/common/gcov/gcov.c new file mode 100644 index 0000000000..58b143de00 --- /dev/null +++ b/xen/common/gcov/gcov.c @@ -0,0 +1,257 @@ +/* + * This code maintains a list of active profiling data structures. + * + * Copyright IBM Corp. 2009 + * Author(s): Peter Oberparleiter + * + * Uses gcc-internal data definitions. + * Based on the gcov-kernel patch by: + * Hubertus Franke + * Nigel Hinds + * Rajan Ravindran + * Peter Oberparleiter + * Paul Larson + * + * Modified for Xen by: + * Wei Liu + */ + +#include +#include +#include + +#include + +#include "gcov.h" + +/** + * gcov_store_uint32 - store 32 bit number in gcov format to buffer + * @buffer: target buffer or NULL + * @off: offset into the buffer + * @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. Returns the number of bytes stored. If @buffer is %NULL, doesn't + * store anything. + */ +size_t gcov_store_uint32(void *buffer, size_t off, uint32_t v) +{ + uint32_t *data; + + if ( buffer ) + { + data = buffer + off; + *data = v; + } + + return sizeof(*data); +} + +/** + * gcov_store_uint64 - store 64 bit number in gcov format to buffer + * @buffer: target buffer or NULL + * @off: offset into the buffer + * @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. Returns the number of bytes stored. If @buffer is %NULL, doesn't store + * anything. + */ +size_t gcov_store_uint64(void *buffer, size_t off, uint64_t v) +{ + uint32_t *data; + + if ( buffer ) + { + data = buffer + off; + + data[0] = (v & 0xffffffffUL); + data[1] = (v >> 32); + } + + return sizeof(*data) * 2; +} + +static size_t gcov_info_payload_size(const struct gcov_info *info) +{ + return gcov_info_to_gcda(NULL, info); +} + +static int gcov_info_dump_payload(const struct gcov_info *info, + XEN_GUEST_HANDLE_PARAM(char) buffer, + uint32_t *off) +{ + char *buf; + uint32_t buf_size; + int ret; + + /* + * Allocate a buffer and dump payload there. This helps us to not + * have copy_to_guest in other functions and retain their simple + * semantics. + */ + + buf_size = gcov_info_payload_size(info); + buf = xmalloc_array(char, buf_size); + + if ( !buf ) + { + ret = -ENOMEM; + goto out; + } + + gcov_info_to_gcda(buf, info); + + if ( copy_to_guest_offset(buffer, *off, buf, buf_size) ) + { + ret = -EFAULT; + goto out; + } + *off += buf_size; + + ret = 0; + out: + xfree(buf); + return ret; + +} + +static uint32_t gcov_get_size(void) +{ + uint32_t total_size = sizeof(uint32_t); /* Magic number XCOV */ + struct gcov_info *info = NULL; + + while ( (info = gcov_info_next(info)) ) + { + /* File name length, including trailing \0 */ + total_size += strlen(gcov_info_filename(info)) + 1; + + /* Payload size field */ + total_size += sizeof(uint32_t); + + /* Payload itself */ + total_size += gcov_info_payload_size(info); + } + + return total_size; +} + +static void gcov_reset_all_counters(void) +{ + struct gcov_info *info = NULL; + + while ( (info = gcov_info_next(info)) ) + gcov_info_reset(info); +} + +static int gcov_dump_one_record(const struct gcov_info *info, + XEN_GUEST_HANDLE_PARAM(char) buffer, + uint32_t *off) +{ + uint32_t payload_size; + uint32_t len; + + /* File name, including trailing \0 */ + len = strlen(gcov_info_filename(info)) + 1; + if ( copy_to_guest_offset(buffer, *off, gcov_info_filename(info), len) ) + return -EFAULT; + *off += len; + + payload_size = gcov_info_payload_size(info); + /* Payload size */ + if ( copy_to_guest_offset(buffer, *off, (char*)&payload_size, + sizeof(uint32_t)) ) + return -EFAULT; + *off += sizeof(uint32_t); + + /* Payload itself */ + return gcov_info_dump_payload(info, buffer, off); +} + +static int gcov_dump_all(XEN_GUEST_HANDLE_PARAM(char) buffer, + uint32_t *buffer_size) +{ + uint32_t off; + uint32_t magic = XEN_GCOV_FORMAT_MAGIC; + struct gcov_info *info = NULL; + int ret; + + if ( *buffer_size < gcov_get_size() ) + { + ret = -ENOBUFS; + goto out; + } + + off = 0; + + /* Magic number */ + if ( copy_to_guest_offset(buffer, off, (char *)&magic, sizeof(magic)) ) + { + ret = -EFAULT; + goto out; + } + off += sizeof(magic); + + while ( (info = gcov_info_next(info)) ) + { + ret = gcov_dump_one_record(info, buffer, &off); + if ( ret ) + goto out; + } + + *buffer_size = off; + + ret = 0; + out: + return ret; +} + +int sysctl_gcov_op(xen_sysctl_gcov_op_t *op) +{ + int ret; + + switch ( op->cmd ) + { + case XEN_SYSCTL_GCOV_get_size: + op->size = gcov_get_size(); + ret = 0; + break; + + case XEN_SYSCTL_GCOV_read: + { + XEN_GUEST_HANDLE_PARAM(char) buf; + uint32_t size = op->size; + + buf = guest_handle_cast(op->buffer, char); + + ret = gcov_dump_all(buf, &size); + op->size = size; + + break; + } + + case XEN_SYSCTL_GCOV_reset: + gcov_reset_all_counters(); + ret = 0; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/gcov/gcov.h b/xen/common/gcov/gcov.h new file mode 100644 index 0000000000..efc1fd7b00 --- /dev/null +++ b/xen/common/gcov/gcov.h @@ -0,0 +1,40 @@ +#ifndef _GCOV_H_ +#define _GCOV_H_ + +#include +#include + +/* + * Profiling data types used for gcc 3.4 and above - these are defined by + * gcc and need to be kept as close to the original definition as possible to + * remain compatible. + */ +#define GCOV_DATA_MAGIC ((unsigned int)0x67636461) +#define GCOV_TAG_FUNCTION ((unsigned int)0x01000000) +#define GCOV_TAG_COUNTER_BASE ((unsigned int)0x01a10000) +#define GCOV_TAG_FOR_COUNTER(count) \ + GCOV_TAG_COUNTER_BASE + ((unsigned int)(count) << 17) + +#define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) + +#if BITS_PER_LONG >= 64 +typedef long gcov_type; +#else +typedef long long gcov_type; +#endif + +/* Opaque gcov_info -- tied to specific gcc gcov formats */ +struct gcov_info; + +void gcov_info_link(struct gcov_info *info); +struct gcov_info *gcov_info_next(const struct gcov_info *info); +void gcov_info_reset(struct gcov_info *info); +const char *gcov_info_filename(const struct gcov_info *info); +size_t gcov_info_to_gcda(char *buffer, const struct gcov_info *info); + +size_t gcov_store_uint32(void *buffer, size_t off, uint32_t v); +size_t gcov_store_uint64(void *buffer, size_t off, uint64_t v); + +#endif /* _GCOV_H_ */ diff --git a/xen/common/gcov/gcov_base.c b/xen/common/gcov/gcov_base.c new file mode 100644 index 0000000000..e5065db5ab --- /dev/null +++ b/xen/common/gcov/gcov_base.c @@ -0,0 +1,60 @@ +/* + * Common code across gcov implementations + * + * Copyright Citrix Systems R&D UK + * Author(s): Wei Liu + * + * Uses gcc-internal data definitions. + * Based on the gcov-kernel patch by: + * Hubertus Franke + * Nigel Hinds + * Rajan Ravindran + * Peter Oberparleiter + * Paul Larson + */ + +#include "gcov.h" + +/* + * __gcov_init is called by gcc-generated constructor code for each object + * file compiled with -fprofile-arcs. + */ +void __init __gcov_init(struct gcov_info *info) +{ + /* Link all gcov info together. */ + gcov_info_link(info); +} + +/* + * These functions may be referenced by gcc-generated profiling code but serve + * no function for Xen. + */ +void __gcov_flush(void) +{ + /* Unused. */ +} + +void __gcov_merge_add(gcov_type *counters, unsigned int n_counters) +{ + /* Unused. */ +} + +void __gcov_merge_single(gcov_type *counters, unsigned int n_counters) +{ + /* Unused. */ +} + +void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters) +{ + /* Unused. */ +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c index 2cac451345..03cca5c55b 100644 --- a/xen/common/sysctl.c +++ b/xen/common/sysctl.c @@ -28,6 +28,7 @@ #include #include #include +#include long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) { @@ -395,6 +396,13 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl) } break; +#ifdef CONFIG_GCOV + case XEN_SYSCTL_gcov_op: + ret = sysctl_gcov_op(&op->u.gcov_op); + copyback = 1; + break; +#endif + #ifdef CONFIG_HAS_PCI case XEN_SYSCTL_pcitopoinfo: { diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h index a939fa9014..28ac56c8b2 100644 --- a/xen/include/public/sysctl.h +++ b/xen/include/public/sysctl.h @@ -36,7 +36,7 @@ #include "physdev.h" #include "tmem.h" -#define XEN_SYSCTL_INTERFACE_VERSION 0x0000000E +#define XEN_SYSCTL_INTERFACE_VERSION 0x0000000F /* * Read console content from Xen buffer ring. @@ -665,6 +665,40 @@ struct xen_sysctl_scheduler_op { typedef struct xen_sysctl_scheduler_op xen_sysctl_scheduler_op_t; DEFINE_XEN_GUEST_HANDLE(xen_sysctl_scheduler_op_t); +/* + * Output format of gcov data: + * + * XEN_GCOV_FORMAT_MAGIC XEN_GCOV_RECORD ... XEN_GCOV_RECORD + * + * That is, one magic number followed by 0 or more record. + * + * The magic number is stored as an uint32_t field. + * + * The record is packed and variable in length. It has the form: + * + * filename: a NULL terminated path name extracted from gcov, used to + * create the name of gcda file. + * size: a uint32_t field indicating the size of the payload, the + * unit is byte. + * payload: the actual payload, length is `size' bytes. + * + * Userspace tool will split the record to different files. + */ + +#define XEN_GCOV_FORMAT_MAGIC 0x58434f56 /* XCOV */ + +#define XEN_SYSCTL_GCOV_get_size 0 /* Get total size of output data */ +#define XEN_SYSCTL_GCOV_read 1 /* Read output data */ +#define XEN_SYSCTL_GCOV_reset 2 /* Reset all counters */ + +struct xen_sysctl_gcov_op { + uint32_t cmd; + uint32_t size; /* IN/OUT: size of the buffer */ + XEN_GUEST_HANDLE_64(char) buffer; /* OUT */ +}; +typedef struct xen_sysctl_gcov_op xen_sysctl_gcov_op_t; +DEFINE_XEN_GUEST_HANDLE(xen_sysctl_gcov_op_t); + #define XEN_SYSCTL_PSR_CMT_get_total_rmid 0 #define XEN_SYSCTL_PSR_CMT_get_l3_upscaling_factor 1 /* The L3 cache size is returned in KB unit */ @@ -1073,7 +1107,7 @@ struct xen_sysctl { #define XEN_SYSCTL_numainfo 17 #define XEN_SYSCTL_cpupool_op 18 #define XEN_SYSCTL_scheduler_op 19 -/* #define XEN_SYSCTL_coverage_op 20 */ +#define XEN_SYSCTL_gcov_op 20 #define XEN_SYSCTL_psr_cmt_op 21 #define XEN_SYSCTL_pcitopoinfo 22 #define XEN_SYSCTL_psr_cat_op 23 @@ -1102,6 +1136,7 @@ struct xen_sysctl { struct xen_sysctl_lockprof_op lockprof_op; struct xen_sysctl_cpupool_op cpupool_op; struct xen_sysctl_scheduler_op scheduler_op; + struct xen_sysctl_gcov_op gcov_op; struct xen_sysctl_psr_cmt_op psr_cmt_op; struct xen_sysctl_psr_cat_op psr_cat_op; struct xen_sysctl_tmem_op tmem_op; diff --git a/xen/include/xen/gcov.h b/xen/include/xen/gcov.h new file mode 100644 index 0000000000..8b5d9140af --- /dev/null +++ b/xen/include/xen/gcov.h @@ -0,0 +1,9 @@ +#ifndef _XEN_GCOV_H +#define _XEN_GCOV_H + +#ifdef CONFIG_GCOV +#include +int sysctl_gcov_op(xen_sysctl_gcov_op_t *op); +#endif + +#endif /* _XEN_GCOV_H */