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---
+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
--- /dev/null
+/*
+ * 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.
+ *
+ * Imported from Linux and modified for Xen by
+ * Wei Liu <wei.liu2@citrix.com>
+ */
+
+
+#include <xen/lib.h>
+
+#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:
+ */
--- /dev/null
+/*
+ * 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 <wei.liu2@citrix.com>
+ */
+
+#include <xen/string.h>
+
+#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:
+ */
--- /dev/null
+/*
+ * 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 <wei.liu2@citrix.com>
+ */
+
+#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:
+ */
--- /dev/null
+/*
+ * 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 <wei.liu2@citrix.com>
+ */
+
+#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:
+ */
--- /dev/null
+/*
+ * This code maintains a list of active profiling data structures.
+ *
+ * Copyright IBM Corp. 2009
+ * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ *
+ * Uses gcc-internal data definitions.
+ * Based on the gcov-kernel patch by:
+ * Hubertus Franke <frankeh@us.ibm.com>
+ * Nigel Hinds <nhinds@us.ibm.com>
+ * Rajan Ravindran <rajancr@us.ibm.com>
+ * Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ * Paul Larson
+ *
+ * Modified for Xen by:
+ * Wei Liu <wei.liu2@citrix.com>
+ */
+
+#include <xen/errno.h>
+#include <xen/guest_access.h>
+#include <xen/types.h>
+
+#include <public/sysctl.h>
+
+#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:
+ */
--- /dev/null
+#ifndef _GCOV_H_
+#define _GCOV_H_
+
+#include <xen/guest_access.h>
+#include <xen/types.h>
+
+/*
+ * 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_ */
--- /dev/null
+/*
+ * Common code across gcov implementations
+ *
+ * Copyright Citrix Systems R&D UK
+ * Author(s): Wei Liu <wei.liu2@citrix.com>
+ *
+ * Uses gcc-internal data definitions.
+ * Based on the gcov-kernel patch by:
+ * Hubertus Franke <frankeh@us.ibm.com>
+ * Nigel Hinds <nhinds@us.ibm.com>
+ * Rajan Ravindran <rajancr@us.ibm.com>
+ * Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ * 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:
+ */
#include <xsm/xsm.h>
#include <xen/pmstat.h>
#include <xen/livepatch.h>
+#include <xen/gcov.h>
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:
{
#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.
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 */
#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
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;
--- /dev/null
+#ifndef _XEN_GCOV_H
+#define _XEN_GCOV_H
+
+#ifdef CONFIG_GCOV
+#include <public/sysctl.h>
+int sysctl_gcov_op(xen_sysctl_gcov_op_t *op);
+#endif
+
+#endif /* _XEN_GCOV_H */