A new sysctl interface for passing gcov data back to userspace. The new
interface uses a customised record file format. The new sysctl reuses
original sysctl number but renames the op to gcov_op.

Formats starting from gcc version 3.4 are supported. The code is
rewritten so that a new format can be easily added in the future.
Version specific code is grouped into different files. The format one
needs to use can be picked via Kconfig. The default format is the newest
one.

Userspace programs to handle extracted data will come in a later patch.

Signed-off-by: Wei Liu <wei.l...@citrix.com>
---
v4:
1. gcov conflicts with livepatch in Kconfig.
2. Finer grain formats

v3:
1. Check gcc version in gcc_*.c files.
2. Eliminate u32 / u64.
3. Mark __gcov_init as __init.
4. Fix a rebase error in v2.
5. Some other cosmetic fixes.

v2:
1. Use tab in Kconfig and indent help text properly.
2. Bump XEN_SYSCTL_INTERFACE_VERSION.
3. Fix cosmetic issues.

Cc: Andrew Cooper <andrew.coop...@citrix.com>
Cc: George Dunlap <george.dun...@eu.citrix.com>
Cc: Ian Jackson <ian.jack...@eu.citrix.com>
Cc: Jan Beulich <jbeul...@suse.com>
Cc: Konrad Rzeszutek Wilk <konrad.w...@oracle.com>
Cc: Stefano Stabellini <sstabell...@kernel.org>
Cc: Tim Deegan <t...@xen.org>
Cc: Wei Liu <wei.l...@citrix.com>
---
 xen/Kconfig.debug           |  39 ++++-
 xen/common/gcov/Makefile    |   5 +
 xen/common/gcov/gcc_3_4.c   | 367 ++++++++++++++++++++++++++++++++++++++++++++
 xen/common/gcov/gcc_4_7.c   | 204 ++++++++++++++++++++++++
 xen/common/gcov/gcc_4_9.c   |  35 +++++
 xen/common/gcov/gcc_5.c     |  35 +++++
 xen/common/gcov/gcov.c      | 257 +++++++++++++++++++++++++++++++
 xen/common/gcov/gcov.h      |  40 +++++
 xen/common/gcov/gcov_base.c |  60 ++++++++
 xen/common/sysctl.c         |   8 +
 xen/include/public/sysctl.h |  39 ++++-
 xen/include/xen/gcov.h      |   9 ++
 12 files changed, 1091 insertions(+), 7 deletions(-)
 create mode 100644 xen/common/gcov/gcc_3_4.c
 create mode 100644 xen/common/gcov/gcc_4_7.c
 create mode 100644 xen/common/gcov/gcc_4_9.c
 create mode 100644 xen/common/gcov/gcc_5.c
 create mode 100644 xen/common/gcov/gcov.c
 create mode 100644 xen/common/gcov/gcov.h
 create mode 100644 xen/common/gcov/gcov_base.c
 create mode 100644 xen/include/xen/gcov.h

diff --git a/xen/Kconfig.debug b/xen/Kconfig.debug
index ef863dc..b19b357 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 e69de29..6a304ab 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 0000000..3631f4b
--- /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 <ober...@linux.vnet.ibm.com>
+ *
+ *    Uses gcc-internal data definitions.
+ *
+ *  Imported from Linux and modified for Xen by
+ *    Wei Liu <wei.l...@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:
+ */
diff --git a/xen/common/gcov/gcc_4_7.c b/xen/common/gcov/gcc_4_7.c
new file mode 100644
index 0000000..25b4a8b
--- /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 <wei.l...@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:
+ */
diff --git a/xen/common/gcov/gcc_4_9.c b/xen/common/gcov/gcc_4_9.c
new file mode 100644
index 0000000..cb5176d
--- /dev/null
+++ b/xen/common/gcov/gcc_4_9.c
@@ -0,0 +1,35 @@
+/*
+ *  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.l...@citrix.com>
+ */
+
+#include "gcov.h"
+
+#if !(GCC_VERSION >= 40900 && GCC_VERSION < 50100)
+#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 0000000..eb89b83
--- /dev/null
+++ b/xen/common/gcov/gcc_5.c
@@ -0,0 +1,35 @@
+/*
+ *  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.l...@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:
+ */
diff --git a/xen/common/gcov/gcov.c b/xen/common/gcov/gcov.c
new file mode 100644
index 0000000..58b143d
--- /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 <ober...@linux.vnet.ibm.com>
+ *
+ *    Uses gcc-internal data definitions.
+ *    Based on the gcov-kernel patch by:
+ *       Hubertus Franke <fran...@us.ibm.com>
+ *       Nigel Hinds <nhi...@us.ibm.com>
+ *       Rajan Ravindran <raja...@us.ibm.com>
+ *       Peter Oberparleiter <ober...@linux.vnet.ibm.com>
+ *       Paul Larson
+ *
+ *  Modified for Xen by:
+ *    Wei Liu <wei.l...@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:
+ */
diff --git a/xen/common/gcov/gcov.h b/xen/common/gcov/gcov.h
new file mode 100644
index 0000000..efc1fd7
--- /dev/null
+++ b/xen/common/gcov/gcov.h
@@ -0,0 +1,40 @@
+#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_ */
diff --git a/xen/common/gcov/gcov_base.c b/xen/common/gcov/gcov_base.c
new file mode 100644
index 0000000..e5065db
--- /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 <wei.l...@citrix.com>
+ *
+ *    Uses gcc-internal data definitions.
+ *    Based on the gcov-kernel patch by:
+ *       Hubertus Franke <fran...@us.ibm.com>
+ *       Nigel Hinds <nhi...@us.ibm.com>
+ *       Rajan Ravindran <raja...@us.ibm.com>
+ *       Peter Oberparleiter <ober...@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:
+ */
diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c
index 93f107c..2f64bb5 100644
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -28,6 +28,7 @@
 #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)
 {
@@ -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 a939fa9..28ac56c 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 0000000..8b5d914
--- /dev/null
+++ b/xen/include/xen/gcov.h
@@ -0,0 +1,9 @@
+#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 */
-- 
2.1.4


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

Reply via email to