summaryrefslogtreecommitdiff
path: root/kernel/gcov
diff options
context:
space:
mode:
authorScott Wood <scottwood@freescale.com>2014-04-07 23:49:35 (GMT)
committerScott Wood <scottwood@freescale.com>2014-04-07 23:49:35 (GMT)
commit62b8c978ee6b8d135d9e7953221de58000dba986 (patch)
tree683b04b2e627f6710c22c151b23c8cc9a165315e /kernel/gcov
parent78fd82238d0e5716578c326404184a27ba67fd6e (diff)
downloadlinux-fsl-qoriq-62b8c978ee6b8d135d9e7953221de58000dba986.tar.xz
Rewind v3.13-rc3+ (78fd82238d0e5716) to v3.12
Diffstat (limited to 'kernel/gcov')
-rw-r--r--kernel/gcov/Kconfig30
-rw-r--r--kernel/gcov/Makefile32
-rw-r--r--kernel/gcov/base.c32
-rw-r--r--kernel/gcov/fs.c52
-rw-r--r--kernel/gcov/gcc_3_4.c115
-rw-r--r--kernel/gcov/gcc_4_7.c560
-rw-r--r--kernel/gcov/gcov.h65
7 files changed, 94 insertions, 792 deletions
diff --git a/kernel/gcov/Kconfig b/kernel/gcov/Kconfig
index d04ce8a..d4da55d 100644
--- a/kernel/gcov/Kconfig
+++ b/kernel/gcov/Kconfig
@@ -46,34 +46,4 @@ config GCOV_PROFILE_ALL
larger and run slower. Also be sure to exclude files from profiling
which are not linked to the kernel image to prevent linker errors.
-choice
- prompt "Specify GCOV format"
- depends on GCOV_KERNEL
- default GCOV_FORMAT_AUTODETECT
- ---help---
- The gcov format is usually determined by the GCC version, but there are
- exceptions where format changes are integrated in lower-version GCCs.
- In such a case use this option to adjust the format used in the kernel
- accordingly.
-
- If unsure, choose "Autodetect".
-
-config GCOV_FORMAT_AUTODETECT
- bool "Autodetect"
- ---help---
- Select this option to use the format that corresponds to your GCC
- version.
-
-config GCOV_FORMAT_3_4
- bool "GCC 3.4 format"
- ---help---
- Select this option to use the format defined by GCC 3.4.
-
-config GCOV_FORMAT_4_7
- bool "GCC 4.7 format"
- ---help---
- Select this option to use the format defined by GCC 4.7.
-
-endchoice
-
endmenu
diff --git a/kernel/gcov/Makefile b/kernel/gcov/Makefile
index 52aa7e8..e97ca59 100644
--- a/kernel/gcov/Makefile
+++ b/kernel/gcov/Makefile
@@ -1,33 +1,3 @@
ccflags-y := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"'
-# if-lt
-# Usage VAR := $(call if-lt, $(a), $(b))
-# Returns 1 if (a < b)
-if-lt = $(shell [ $(1) -lt $(2) ] && echo 1)
-
-ifeq ($(CONFIG_GCOV_FORMAT_3_4),y)
- cc-ver := 0304
-else ifeq ($(CONFIG_GCOV_FORMAT_4_7),y)
- cc-ver := 0407
-else
-# Use cc-version if available, otherwise set 0
-#
-# scripts/Kbuild.include, which contains cc-version function, is not included
-# during make clean "make -f scripts/Makefile.clean obj=kernel/gcov"
-# Meaning cc-ver is empty causing if-lt test to fail with
-# "/bin/sh: line 0: [: -lt: unary operator expected" error mesage.
-# This has no affect on the clean phase, but the error message could be
-# confusing/annoying. So this dummy workaround sets cc-ver to zero if cc-version
-# is not available. We can probably move if-lt to Kbuild.include, so it's also
-# not defined during clean or to include Kbuild.include in
-# scripts/Makefile.clean. But the following workaround seems least invasive.
- cc-ver := $(if $(call cc-version),$(call cc-version),0)
-endif
-
-obj-$(CONFIG_GCOV_KERNEL) := base.o fs.o
-
-ifeq ($(call if-lt, $(cc-ver), 0407),1)
- obj-$(CONFIG_GCOV_KERNEL) += gcc_3_4.o
-else
- obj-$(CONFIG_GCOV_KERNEL) += gcc_4_7.o
-endif
+obj-$(CONFIG_GCOV_KERNEL) := base.o fs.o gcc_3_4.o
diff --git a/kernel/gcov/base.c b/kernel/gcov/base.c
index f45b75b..9b22d03 100644
--- a/kernel/gcov/base.c
+++ b/kernel/gcov/base.c
@@ -20,6 +20,7 @@
#include <linux/mutex.h>
#include "gcov.h"
+static struct gcov_info *gcov_info_head;
static int gcov_events_enabled;
static DEFINE_MUTEX(gcov_lock);
@@ -33,7 +34,7 @@ void __gcov_init(struct gcov_info *info)
mutex_lock(&gcov_lock);
if (gcov_version == 0) {
- gcov_version = gcov_info_version(info);
+ gcov_version = info->version;
/*
* Printing gcc's version magic may prove useful for debugging
* incompatibility reports.
@@ -44,7 +45,8 @@ void __gcov_init(struct gcov_info *info)
* Add new profiling data structure to list and inform event
* listener.
*/
- gcov_info_link(info);
+ info->next = gcov_info_head;
+ gcov_info_head = info;
if (gcov_events_enabled)
gcov_event(GCOV_ADD, info);
mutex_unlock(&gcov_lock);
@@ -79,12 +81,6 @@ void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters)
}
EXPORT_SYMBOL(__gcov_merge_delta);
-void __gcov_merge_ior(gcov_type *counters, unsigned int n_counters)
-{
- /* Unused. */
-}
-EXPORT_SYMBOL(__gcov_merge_ior);
-
/**
* gcov_enable_events - enable event reporting through gcov_event()
*
@@ -95,15 +91,13 @@ EXPORT_SYMBOL(__gcov_merge_ior);
*/
void gcov_enable_events(void)
{
- struct gcov_info *info = NULL;
+ struct gcov_info *info;
mutex_lock(&gcov_lock);
gcov_events_enabled = 1;
-
/* Perform event callback for previously registered entries. */
- while ((info = gcov_info_next(info)))
+ for (info = gcov_info_head; info; info = info->next)
gcov_event(GCOV_ADD, info);
-
mutex_unlock(&gcov_lock);
}
@@ -118,23 +112,25 @@ static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
void *data)
{
struct module *mod = data;
- struct gcov_info *info = NULL;
- struct gcov_info *prev = NULL;
+ struct gcov_info *info;
+ struct gcov_info *prev;
if (event != MODULE_STATE_GOING)
return NOTIFY_OK;
mutex_lock(&gcov_lock);
-
+ prev = NULL;
/* Remove entries located in module from linked list. */
- while ((info = gcov_info_next(info))) {
+ for (info = gcov_info_head; info; info = info->next) {
if (within(info, mod->module_core, mod->core_size)) {
- gcov_info_unlink(prev, info);
+ if (prev)
+ prev->next = info->next;
+ else
+ gcov_info_head = info->next;
if (gcov_events_enabled)
gcov_event(GCOV_REMOVE, info);
} else
prev = info;
}
-
mutex_unlock(&gcov_lock);
return NOTIFY_OK;
diff --git a/kernel/gcov/fs.c b/kernel/gcov/fs.c
index 15ff01a..7a7d2ee 100644
--- a/kernel/gcov/fs.c
+++ b/kernel/gcov/fs.c
@@ -75,7 +75,7 @@ static int __init gcov_persist_setup(char *str)
unsigned long val;
if (kstrtoul(str, 0, &val)) {
- pr_warn("invalid gcov_persist parameter '%s'\n", str);
+ pr_warning("invalid gcov_persist parameter '%s'\n", str);
return 0;
}
gcov_persist = val;
@@ -242,7 +242,7 @@ static struct gcov_node *get_node_by_name(const char *name)
list_for_each_entry(node, &all_head, all) {
info = get_node_info(node);
- if (info && (strcmp(gcov_info_filename(info), name) == 0))
+ if (info && (strcmp(info->filename, name) == 0))
return node;
}
@@ -279,7 +279,7 @@ static ssize_t gcov_seq_write(struct file *file, const char __user *addr,
seq = file->private_data;
info = gcov_iter_get_info(seq->private);
mutex_lock(&node_lock);
- node = get_node_by_name(gcov_info_filename(info));
+ node = get_node_by_name(info->filename);
if (node) {
/* Reset counts or remove node for unloaded modules. */
if (node->num_loaded == 0)
@@ -365,7 +365,7 @@ static const char *deskew(const char *basename)
*/
static void add_links(struct gcov_node *node, struct dentry *parent)
{
- const char *basename;
+ char *basename;
char *target;
int num;
int i;
@@ -376,14 +376,14 @@ static void add_links(struct gcov_node *node, struct dentry *parent)
if (!node->links)
return;
for (i = 0; i < num; i++) {
- target = get_link_target(
- gcov_info_filename(get_node_info(node)),
- &gcov_link[i]);
+ target = get_link_target(get_node_info(node)->filename,
+ &gcov_link[i]);
if (!target)
goto out_err;
- basename = kbasename(target);
- if (basename == target)
+ basename = strrchr(target, '/');
+ if (!basename)
goto out_err;
+ basename++;
node->links[i] = debugfs_create_symlink(deskew(basename),
parent, target);
if (!node->links[i])
@@ -450,7 +450,7 @@ static struct gcov_node *new_node(struct gcov_node *parent,
} else
node->dentry = debugfs_create_dir(node->name, parent->dentry);
if (!node->dentry) {
- pr_warn("could not create file\n");
+ pr_warning("could not create file\n");
kfree(node);
return NULL;
}
@@ -463,7 +463,7 @@ static struct gcov_node *new_node(struct gcov_node *parent,
err_nomem:
kfree(node);
- pr_warn("out of memory\n");
+ pr_warning("out of memory\n");
return NULL;
}
@@ -576,7 +576,7 @@ static void add_node(struct gcov_info *info)
struct gcov_node *parent;
struct gcov_node *node;
- filename = kstrdup(gcov_info_filename(info), GFP_KERNEL);
+ filename = kstrdup(info->filename, GFP_KERNEL);
if (!filename)
return;
parent = &root_node;
@@ -630,8 +630,8 @@ static void add_info(struct gcov_node *node, struct gcov_info *info)
*/
loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL);
if (!loaded_info) {
- pr_warn("could not add '%s' (out of memory)\n",
- gcov_info_filename(info));
+ pr_warning("could not add '%s' (out of memory)\n",
+ info->filename);
return;
}
memcpy(loaded_info, node->loaded_info,
@@ -644,9 +644,8 @@ static void add_info(struct gcov_node *node, struct gcov_info *info)
* data set replaces the copy of the last one.
*/
if (!gcov_info_is_compatible(node->unloaded_info, info)) {
- pr_warn("discarding saved data for %s "
- "(incompatible version)\n",
- gcov_info_filename(info));
+ pr_warning("discarding saved data for %s "
+ "(incompatible version)\n", info->filename);
gcov_info_free(node->unloaded_info);
node->unloaded_info = NULL;
}
@@ -656,8 +655,8 @@ static void add_info(struct gcov_node *node, struct gcov_info *info)
* The initial one takes precedence.
*/
if (!gcov_info_is_compatible(node->loaded_info[0], info)) {
- pr_warn("could not add '%s' (incompatible "
- "version)\n", gcov_info_filename(info));
+ pr_warning("could not add '%s' (incompatible "
+ "version)\n", info->filename);
kfree(loaded_info);
return;
}
@@ -692,9 +691,8 @@ static void save_info(struct gcov_node *node, struct gcov_info *info)
else {
node->unloaded_info = gcov_info_dup(info);
if (!node->unloaded_info) {
- pr_warn("could not save data for '%s' "
- "(out of memory)\n",
- gcov_info_filename(info));
+ pr_warning("could not save data for '%s' "
+ "(out of memory)\n", info->filename);
}
}
}
@@ -709,8 +707,8 @@ static void remove_info(struct gcov_node *node, struct gcov_info *info)
i = get_info_index(node, info);
if (i < 0) {
- pr_warn("could not remove '%s' (not found)\n",
- gcov_info_filename(info));
+ pr_warning("could not remove '%s' (not found)\n",
+ info->filename);
return;
}
if (gcov_persist)
@@ -737,7 +735,7 @@ void gcov_event(enum gcov_action action, struct gcov_info *info)
struct gcov_node *node;
mutex_lock(&node_lock);
- node = get_node_by_name(gcov_info_filename(info));
+ node = get_node_by_name(info->filename);
switch (action) {
case GCOV_ADD:
if (node)
@@ -749,8 +747,8 @@ void gcov_event(enum gcov_action action, struct gcov_info *info)
if (node)
remove_info(node, info);
else {
- pr_warn("could not remove '%s' (not found)\n",
- gcov_info_filename(info));
+ pr_warning("could not remove '%s' (not found)\n",
+ info->filename);
}
break;
}
diff --git a/kernel/gcov/gcc_3_4.c b/kernel/gcov/gcc_3_4.c
index 27bc88a..ae5bb42 100644
--- a/kernel/gcov/gcc_3_4.c
+++ b/kernel/gcov/gcc_3_4.c
@@ -21,121 +21,6 @@
#include <linux/vmalloc.h>
#include "gcov.h"
-#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];
-};
-
-/**
- * gcov_info_filename - return info filename
- * @info: profiling data set
- */
-const char *gcov_info_filename(struct gcov_info *info)
-{
- return info->filename;
-}
-
-/**
- * gcov_info_version - return info version
- * @info: profiling data set
- */
-unsigned int gcov_info_version(struct gcov_info *info)
-{
- return info->version;
-}
-
-/**
- * gcov_info_next - return next profiling data set
- * @info: profiling data set
- *
- * Returns next gcov_info following @info or first gcov_info in the chain if
- * @info is %NULL.
- */
-struct gcov_info *gcov_info_next(struct gcov_info *info)
-{
- if (!info)
- return gcov_info_head;
-
- return info->next;
-}
-
-/**
- * gcov_info_link - link/add profiling data set to the list
- * @info: profiling data set
- */
-void gcov_info_link(struct gcov_info *info)
-{
- info->next = gcov_info_head;
- gcov_info_head = info;
-}
-
-/**
- * gcov_info_unlink - unlink/remove profiling data set from the list
- * @prev: previous profiling data set
- * @info: profiling data set
- */
-void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
-{
- if (prev)
- prev->next = info->next;
- else
- gcov_info_head = info->next;
-}
-
/* Symbolic links to be created for each profiling data file. */
const struct gcov_link gcov_link[] = {
{ OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
diff --git a/kernel/gcov/gcc_4_7.c b/kernel/gcov/gcc_4_7.c
deleted file mode 100644
index 2c6e463..0000000
--- a/kernel/gcov/gcc_4_7.c
+++ /dev/null
@@ -1,560 +0,0 @@
-/*
- * 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.
- */
-
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/seq_file.h>
-#include <linux/vmalloc.h>
-#include "gcov.h"
-
-#define GCOV_COUNTERS 8
-#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;
-};
-
-/**
- * gcov_info_filename - return info filename
- * @info: profiling data set
- */
-const char *gcov_info_filename(struct gcov_info *info)
-{
- return info->filename;
-}
-
-/**
- * gcov_info_version - return info version
- * @info: profiling data set
- */
-unsigned int gcov_info_version(struct gcov_info *info)
-{
- return info->version;
-}
-
-/**
- * gcov_info_next - return next profiling data set
- * @info: profiling data set
- *
- * Returns next gcov_info following @info or first gcov_info in the chain if
- * @info is %NULL.
- */
-struct gcov_info *gcov_info_next(struct gcov_info *info)
-{
- if (!info)
- return gcov_info_head;
-
- return info->next;
-}
-
-/**
- * gcov_info_link - link/add profiling data set to the list
- * @info: profiling data set
- */
-void gcov_info_link(struct gcov_info *info)
-{
- info->next = gcov_info_head;
- gcov_info_head = info;
-}
-
-/**
- * gcov_info_unlink - unlink/remove profiling data set from the list
- * @prev: previous profiling data set
- * @info: profiling data set
- */
-void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
-{
- if (prev)
- prev->next = info->next;
- else
- gcov_info_head = info->next;
-}
-
-/* Symbolic links to be created for each profiling data file. */
-const struct gcov_link gcov_link[] = {
- { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
- { 0, NULL},
-};
-
-/*
- * Determine whether a counter is active. Doesn't change at run-time.
- */
-static int counter_active(struct gcov_info *info, unsigned int type)
-{
- return info->merge[type] ? 1 : 0;
-}
-
-/* Determine number of active counters. Based on gcc magic. */
-static unsigned int num_counter_active(struct gcov_info *info)
-{
- unsigned int i;
- unsigned int result = 0;
-
- for (i = 0; i < GCOV_COUNTERS; i++) {
- if (counter_active(info, i))
- result++;
- }
- return result;
-}
-
-/**
- * gcov_info_reset - reset profiling data to zero
- * @info: profiling data set
- */
-void gcov_info_reset(struct gcov_info *info)
-{
- 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++;
- }
- }
-}
-
-/**
- * gcov_info_is_compatible - check if profiling data can be added
- * @info1: first profiling data set
- * @info2: second profiling data set
- *
- * Returns non-zero if profiling data can be added, zero otherwise.
- */
-int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
-{
- return (info1->stamp == info2->stamp);
-}
-
-/**
- * gcov_info_add - add up profiling data
- * @dest: profiling data set to which data is added
- * @source: profiling data set which is added
- *
- * Adds profiling counts of @source to @dest.
- */
-void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
-{
- struct gcov_ctr_info *dci_ptr;
- struct gcov_ctr_info *sci_ptr;
- unsigned int fi_idx;
- unsigned int ct_idx;
- unsigned int val_idx;
-
- for (fi_idx = 0; fi_idx < src->n_functions; fi_idx++) {
- dci_ptr = dst->functions[fi_idx]->ctrs;
- sci_ptr = src->functions[fi_idx]->ctrs;
-
- for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
- if (!counter_active(src, ct_idx))
- continue;
-
- for (val_idx = 0; val_idx < sci_ptr->num; val_idx++)
- dci_ptr->values[val_idx] +=
- sci_ptr->values[val_idx];
-
- dci_ptr++;
- sci_ptr++;
- }
- }
-}
-
-/**
- * gcov_info_dup - duplicate profiling data set
- * @info: profiling data set to duplicate
- *
- * Return newly allocated duplicate on success, %NULL on error.
- */
-struct gcov_info *gcov_info_dup(struct gcov_info *info)
-{
- struct gcov_info *dup;
- struct gcov_ctr_info *dci_ptr; /* dst counter info */
- struct gcov_ctr_info *sci_ptr; /* src counter info */
- unsigned int active;
- unsigned int fi_idx; /* function info idx */
- unsigned int ct_idx; /* counter type idx */
- size_t fi_size; /* function info size */
- size_t cv_size; /* counter values size */
-
- dup = kmemdup(info, sizeof(*dup), GFP_KERNEL);
- if (!dup)
- return NULL;
-
- dup->next = NULL;
- dup->filename = NULL;
- dup->functions = NULL;
-
- dup->filename = kstrdup(info->filename, GFP_KERNEL);
- if (!dup->filename)
- goto err_free;
-
- dup->functions = kcalloc(info->n_functions,
- sizeof(struct gcov_fn_info *), GFP_KERNEL);
- if (!dup->functions)
- goto err_free;
-
- active = num_counter_active(info);
- fi_size = sizeof(struct gcov_fn_info);
- fi_size += sizeof(struct gcov_ctr_info) * active;
-
- for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
- dup->functions[fi_idx] = kzalloc(fi_size, GFP_KERNEL);
- if (!dup->functions[fi_idx])
- goto err_free;
-
- *(dup->functions[fi_idx]) = *(info->functions[fi_idx]);
-
- sci_ptr = info->functions[fi_idx]->ctrs;
- dci_ptr = dup->functions[fi_idx]->ctrs;
-
- for (ct_idx = 0; ct_idx < active; ct_idx++) {
-
- cv_size = sizeof(gcov_type) * sci_ptr->num;
-
- dci_ptr->values = vmalloc(cv_size);
-
- if (!dci_ptr->values)
- goto err_free;
-
- dci_ptr->num = sci_ptr->num;
- memcpy(dci_ptr->values, sci_ptr->values, cv_size);
-
- sci_ptr++;
- dci_ptr++;
- }
- }
-
- return dup;
-err_free:
- gcov_info_free(dup);
- return NULL;
-}
-
-/**
- * gcov_info_free - release memory for profiling data set duplicate
- * @info: profiling data set duplicate to free
- */
-void gcov_info_free(struct gcov_info *info)
-{
- unsigned int active;
- unsigned int fi_idx;
- unsigned int ct_idx;
- struct gcov_ctr_info *ci_ptr;
-
- if (!info->functions)
- goto free_info;
-
- active = num_counter_active(info);
-
- for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
- if (!info->functions[fi_idx])
- continue;
-
- ci_ptr = info->functions[fi_idx]->ctrs;
-
- for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++)
- vfree(ci_ptr->values);
-
- kfree(info->functions[fi_idx]);
- }
-
-free_info:
- kfree(info->functions);
- kfree(info->filename);
- kfree(info);
-}
-
-#define ITER_STRIDE PAGE_SIZE
-
-/**
- * struct gcov_iterator - specifies current file position in logical records
- * @info: associated profiling data
- * @buffer: buffer containing file data
- * @size: size of buffer
- * @pos: current position in file
- */
-struct gcov_iterator {
- struct gcov_info *info;
- void *buffer;
- size_t size;
- loff_t pos;
-};
-
-/**
- * store_gcov_u32 - 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.
- */
-static size_t store_gcov_u32(void *buffer, size_t off, u32 v)
-{
- u32 *data;
-
- if (buffer) {
- data = buffer + off;
- *data = v;
- }
-
- return sizeof(*data);
-}
-
-/**
- * store_gcov_u64 - 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.
- */
-static size_t store_gcov_u64(void *buffer, size_t off, u64 v)
-{
- u32 *data;
-
- if (buffer) {
- data = buffer + off;
-
- data[0] = (v & 0xffffffffUL);
- data[1] = (v >> 32);
- }
-
- return sizeof(*data) * 2;
-}
-
-/**
- * convert_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.
- */
-static size_t convert_to_gcda(char *buffer, 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 += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
- pos += store_gcov_u32(buffer, pos, info->version);
- pos += store_gcov_u32(buffer, pos, info->stamp);
-
- for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
- fi_ptr = info->functions[fi_idx];
-
- /* Function record. */
- pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
- pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH);
- pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
- pos += store_gcov_u32(buffer, pos, fi_ptr->lineno_checksum);
- pos += store_gcov_u32(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 += store_gcov_u32(buffer, pos,
- GCOV_TAG_FOR_COUNTER(ct_idx));
- pos += store_gcov_u32(buffer, pos, ci_ptr->num * 2);
-
- for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
- pos += store_gcov_u64(buffer, pos,
- ci_ptr->values[cv_idx]);
- }
-
- ci_ptr++;
- }
- }
-
- return pos;
-}
-
-/**
- * gcov_iter_new - allocate and initialize profiling data iterator
- * @info: profiling data set to be iterated
- *
- * Return file iterator on success, %NULL otherwise.
- */
-struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
-{
- struct gcov_iterator *iter;
-
- iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL);
- if (!iter)
- goto err_free;
-
- iter->info = info;
- /* Dry-run to get the actual buffer size. */
- iter->size = convert_to_gcda(NULL, info);
- iter->buffer = vmalloc(iter->size);
- if (!iter->buffer)
- goto err_free;
-
- convert_to_gcda(iter->buffer, info);
-
- return iter;
-
-err_free:
- kfree(iter);
- return NULL;
-}
-
-
-/**
- * gcov_iter_get_info - return profiling data set for given file iterator
- * @iter: file iterator
- */
-void gcov_iter_free(struct gcov_iterator *iter)
-{
- vfree(iter->buffer);
- kfree(iter);
-}
-
-/**
- * gcov_iter_get_info - return profiling data set for given file iterator
- * @iter: file iterator
- */
-struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
-{
- return iter->info;
-}
-
-/**
- * gcov_iter_start - reset file iterator to starting position
- * @iter: file iterator
- */
-void gcov_iter_start(struct gcov_iterator *iter)
-{
- iter->pos = 0;
-}
-
-/**
- * gcov_iter_next - advance file iterator to next logical record
- * @iter: file iterator
- *
- * Return zero if new position is valid, non-zero if iterator has reached end.
- */
-int gcov_iter_next(struct gcov_iterator *iter)
-{
- if (iter->pos < iter->size)
- iter->pos += ITER_STRIDE;
-
- if (iter->pos >= iter->size)
- return -EINVAL;
-
- return 0;
-}
-
-/**
- * gcov_iter_write - write data for current pos to seq_file
- * @iter: file iterator
- * @seq: seq_file handle
- *
- * Return zero on success, non-zero otherwise.
- */
-int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
-{
- size_t len;
-
- if (iter->pos >= iter->size)
- return -EINVAL;
-
- len = ITER_STRIDE;
- if (iter->pos + len > iter->size)
- len = iter->size - iter->pos;
-
- seq_write(seq, iter->buffer + iter->pos, len);
-
- return 0;
-}
diff --git a/kernel/gcov/gcov.h b/kernel/gcov/gcov.h
index 92c8e22..060073e 100644
--- a/kernel/gcov/gcov.h
+++ b/kernel/gcov/gcov.h
@@ -21,6 +21,7 @@
* gcc and need to be kept as close to the original definition as possible to
* remain compatible.
*/
+#define GCOV_COUNTERS 5
#define GCOV_DATA_MAGIC ((unsigned int) 0x67636461)
#define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000)
#define GCOV_TAG_COUNTER_BASE ((unsigned int) 0x01a10000)
@@ -33,18 +34,60 @@ typedef long gcov_type;
typedef long long gcov_type;
#endif
-/* Opaque gcov_info. The gcov structures can change as for example in gcc 4.7 so
- * we cannot use full definition here and they need to be placed in gcc specific
- * implementation of gcov. This also means no direct access to the members in
- * generic code and usage of the interface below.*/
-struct gcov_info;
+/**
+ * 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);
+};
-/* Interface to access gcov_info data */
-const char *gcov_info_filename(struct gcov_info *info);
-unsigned int gcov_info_version(struct gcov_info *info);
-struct gcov_info *gcov_info_next(struct gcov_info *info);
-void gcov_info_link(struct gcov_info *info);
-void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info);
+/**
+ * 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];
+};
/* Base interface. */
enum gcov_action {