aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/gcov
diff options
context:
space:
mode:
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, 792 insertions, 94 deletions
diff --git a/kernel/gcov/Kconfig b/kernel/gcov/Kconfig
index d4da55d1fb65..d04ce8ac4399 100644
--- a/kernel/gcov/Kconfig
+++ b/kernel/gcov/Kconfig
@@ -46,4 +46,34 @@ config GCOV_PROFILE_ALL
46 larger and run slower. Also be sure to exclude files from profiling 46 larger and run slower. Also be sure to exclude files from profiling
47 which are not linked to the kernel image to prevent linker errors. 47 which are not linked to the kernel image to prevent linker errors.
48 48
49choice
50 prompt "Specify GCOV format"
51 depends on GCOV_KERNEL
52 default GCOV_FORMAT_AUTODETECT
53 ---help---
54 The gcov format is usually determined by the GCC version, but there are
55 exceptions where format changes are integrated in lower-version GCCs.
56 In such a case use this option to adjust the format used in the kernel
57 accordingly.
58
59 If unsure, choose "Autodetect".
60
61config GCOV_FORMAT_AUTODETECT
62 bool "Autodetect"
63 ---help---
64 Select this option to use the format that corresponds to your GCC
65 version.
66
67config GCOV_FORMAT_3_4
68 bool "GCC 3.4 format"
69 ---help---
70 Select this option to use the format defined by GCC 3.4.
71
72config GCOV_FORMAT_4_7
73 bool "GCC 4.7 format"
74 ---help---
75 Select this option to use the format defined by GCC 4.7.
76
77endchoice
78
49endmenu 79endmenu
diff --git a/kernel/gcov/Makefile b/kernel/gcov/Makefile
index e97ca59e2520..52aa7e8de927 100644
--- a/kernel/gcov/Makefile
+++ b/kernel/gcov/Makefile
@@ -1,3 +1,33 @@
1ccflags-y := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"' 1ccflags-y := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"'
2 2
3obj-$(CONFIG_GCOV_KERNEL) := base.o fs.o gcc_3_4.o 3# if-lt
4# Usage VAR := $(call if-lt, $(a), $(b))
5# Returns 1 if (a < b)
6if-lt = $(shell [ $(1) -lt $(2) ] && echo 1)
7
8ifeq ($(CONFIG_GCOV_FORMAT_3_4),y)
9 cc-ver := 0304
10else ifeq ($(CONFIG_GCOV_FORMAT_4_7),y)
11 cc-ver := 0407
12else
13# Use cc-version if available, otherwise set 0
14#
15# scripts/Kbuild.include, which contains cc-version function, is not included
16# during make clean "make -f scripts/Makefile.clean obj=kernel/gcov"
17# Meaning cc-ver is empty causing if-lt test to fail with
18# "/bin/sh: line 0: [: -lt: unary operator expected" error mesage.
19# This has no affect on the clean phase, but the error message could be
20# confusing/annoying. So this dummy workaround sets cc-ver to zero if cc-version
21# is not available. We can probably move if-lt to Kbuild.include, so it's also
22# not defined during clean or to include Kbuild.include in
23# scripts/Makefile.clean. But the following workaround seems least invasive.
24 cc-ver := $(if $(call cc-version),$(call cc-version),0)
25endif
26
27obj-$(CONFIG_GCOV_KERNEL) := base.o fs.o
28
29ifeq ($(call if-lt, $(cc-ver), 0407),1)
30 obj-$(CONFIG_GCOV_KERNEL) += gcc_3_4.o
31else
32 obj-$(CONFIG_GCOV_KERNEL) += gcc_4_7.o
33endif
diff --git a/kernel/gcov/base.c b/kernel/gcov/base.c
index 9b22d03cc581..f45b75b713c0 100644
--- a/kernel/gcov/base.c
+++ b/kernel/gcov/base.c
@@ -20,7 +20,6 @@
20#include <linux/mutex.h> 20#include <linux/mutex.h>
21#include "gcov.h" 21#include "gcov.h"
22 22
23static struct gcov_info *gcov_info_head;
24static int gcov_events_enabled; 23static int gcov_events_enabled;
25static DEFINE_MUTEX(gcov_lock); 24static DEFINE_MUTEX(gcov_lock);
26 25
@@ -34,7 +33,7 @@ void __gcov_init(struct gcov_info *info)
34 33
35 mutex_lock(&gcov_lock); 34 mutex_lock(&gcov_lock);
36 if (gcov_version == 0) { 35 if (gcov_version == 0) {
37 gcov_version = info->version; 36 gcov_version = gcov_info_version(info);
38 /* 37 /*
39 * Printing gcc's version magic may prove useful for debugging 38 * Printing gcc's version magic may prove useful for debugging
40 * incompatibility reports. 39 * incompatibility reports.
@@ -45,8 +44,7 @@ void __gcov_init(struct gcov_info *info)
45 * Add new profiling data structure to list and inform event 44 * Add new profiling data structure to list and inform event
46 * listener. 45 * listener.
47 */ 46 */
48 info->next = gcov_info_head; 47 gcov_info_link(info);
49 gcov_info_head = info;
50 if (gcov_events_enabled) 48 if (gcov_events_enabled)
51 gcov_event(GCOV_ADD, info); 49 gcov_event(GCOV_ADD, info);
52 mutex_unlock(&gcov_lock); 50 mutex_unlock(&gcov_lock);
@@ -81,6 +79,12 @@ void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters)
81} 79}
82EXPORT_SYMBOL(__gcov_merge_delta); 80EXPORT_SYMBOL(__gcov_merge_delta);
83 81
82void __gcov_merge_ior(gcov_type *counters, unsigned int n_counters)
83{
84 /* Unused. */
85}
86EXPORT_SYMBOL(__gcov_merge_ior);
87
84/** 88/**
85 * gcov_enable_events - enable event reporting through gcov_event() 89 * gcov_enable_events - enable event reporting through gcov_event()
86 * 90 *
@@ -91,13 +95,15 @@ EXPORT_SYMBOL(__gcov_merge_delta);
91 */ 95 */
92void gcov_enable_events(void) 96void gcov_enable_events(void)
93{ 97{
94 struct gcov_info *info; 98 struct gcov_info *info = NULL;
95 99
96 mutex_lock(&gcov_lock); 100 mutex_lock(&gcov_lock);
97 gcov_events_enabled = 1; 101 gcov_events_enabled = 1;
102
98 /* Perform event callback for previously registered entries. */ 103 /* Perform event callback for previously registered entries. */
99 for (info = gcov_info_head; info; info = info->next) 104 while ((info = gcov_info_next(info)))
100 gcov_event(GCOV_ADD, info); 105 gcov_event(GCOV_ADD, info);
106
101 mutex_unlock(&gcov_lock); 107 mutex_unlock(&gcov_lock);
102} 108}
103 109
@@ -112,25 +118,23 @@ static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
112 void *data) 118 void *data)
113{ 119{
114 struct module *mod = data; 120 struct module *mod = data;
115 struct gcov_info *info; 121 struct gcov_info *info = NULL;
116 struct gcov_info *prev; 122 struct gcov_info *prev = NULL;
117 123
118 if (event != MODULE_STATE_GOING) 124 if (event != MODULE_STATE_GOING)
119 return NOTIFY_OK; 125 return NOTIFY_OK;
120 mutex_lock(&gcov_lock); 126 mutex_lock(&gcov_lock);
121 prev = NULL; 127
122 /* Remove entries located in module from linked list. */ 128 /* Remove entries located in module from linked list. */
123 for (info = gcov_info_head; info; info = info->next) { 129 while ((info = gcov_info_next(info))) {
124 if (within(info, mod->module_core, mod->core_size)) { 130 if (within(info, mod->module_core, mod->core_size)) {
125 if (prev) 131 gcov_info_unlink(prev, info);
126 prev->next = info->next;
127 else
128 gcov_info_head = info->next;
129 if (gcov_events_enabled) 132 if (gcov_events_enabled)
130 gcov_event(GCOV_REMOVE, info); 133 gcov_event(GCOV_REMOVE, info);
131 } else 134 } else
132 prev = info; 135 prev = info;
133 } 136 }
137
134 mutex_unlock(&gcov_lock); 138 mutex_unlock(&gcov_lock);
135 139
136 return NOTIFY_OK; 140 return NOTIFY_OK;
diff --git a/kernel/gcov/fs.c b/kernel/gcov/fs.c
index 7a7d2ee96d42..15ff01a76379 100644
--- a/kernel/gcov/fs.c
+++ b/kernel/gcov/fs.c
@@ -75,7 +75,7 @@ static int __init gcov_persist_setup(char *str)
75 unsigned long val; 75 unsigned long val;
76 76
77 if (kstrtoul(str, 0, &val)) { 77 if (kstrtoul(str, 0, &val)) {
78 pr_warning("invalid gcov_persist parameter '%s'\n", str); 78 pr_warn("invalid gcov_persist parameter '%s'\n", str);
79 return 0; 79 return 0;
80 } 80 }
81 gcov_persist = val; 81 gcov_persist = val;
@@ -242,7 +242,7 @@ static struct gcov_node *get_node_by_name(const char *name)
242 242
243 list_for_each_entry(node, &all_head, all) { 243 list_for_each_entry(node, &all_head, all) {
244 info = get_node_info(node); 244 info = get_node_info(node);
245 if (info && (strcmp(info->filename, name) == 0)) 245 if (info && (strcmp(gcov_info_filename(info), name) == 0))
246 return node; 246 return node;
247 } 247 }
248 248
@@ -279,7 +279,7 @@ static ssize_t gcov_seq_write(struct file *file, const char __user *addr,
279 seq = file->private_data; 279 seq = file->private_data;
280 info = gcov_iter_get_info(seq->private); 280 info = gcov_iter_get_info(seq->private);
281 mutex_lock(&node_lock); 281 mutex_lock(&node_lock);
282 node = get_node_by_name(info->filename); 282 node = get_node_by_name(gcov_info_filename(info));
283 if (node) { 283 if (node) {
284 /* Reset counts or remove node for unloaded modules. */ 284 /* Reset counts or remove node for unloaded modules. */
285 if (node->num_loaded == 0) 285 if (node->num_loaded == 0)
@@ -365,7 +365,7 @@ static const char *deskew(const char *basename)
365 */ 365 */
366static void add_links(struct gcov_node *node, struct dentry *parent) 366static void add_links(struct gcov_node *node, struct dentry *parent)
367{ 367{
368 char *basename; 368 const char *basename;
369 char *target; 369 char *target;
370 int num; 370 int num;
371 int i; 371 int i;
@@ -376,14 +376,14 @@ static void add_links(struct gcov_node *node, struct dentry *parent)
376 if (!node->links) 376 if (!node->links)
377 return; 377 return;
378 for (i = 0; i < num; i++) { 378 for (i = 0; i < num; i++) {
379 target = get_link_target(get_node_info(node)->filename, 379 target = get_link_target(
380 &gcov_link[i]); 380 gcov_info_filename(get_node_info(node)),
381 &gcov_link[i]);
381 if (!target) 382 if (!target)
382 goto out_err; 383 goto out_err;
383 basename = strrchr(target, '/'); 384 basename = kbasename(target);
384 if (!basename) 385 if (basename == target)
385 goto out_err; 386 goto out_err;
386 basename++;
387 node->links[i] = debugfs_create_symlink(deskew(basename), 387 node->links[i] = debugfs_create_symlink(deskew(basename),
388 parent, target); 388 parent, target);
389 if (!node->links[i]) 389 if (!node->links[i])
@@ -450,7 +450,7 @@ static struct gcov_node *new_node(struct gcov_node *parent,
450 } else 450 } else
451 node->dentry = debugfs_create_dir(node->name, parent->dentry); 451 node->dentry = debugfs_create_dir(node->name, parent->dentry);
452 if (!node->dentry) { 452 if (!node->dentry) {
453 pr_warning("could not create file\n"); 453 pr_warn("could not create file\n");
454 kfree(node); 454 kfree(node);
455 return NULL; 455 return NULL;
456 } 456 }
@@ -463,7 +463,7 @@ static struct gcov_node *new_node(struct gcov_node *parent,
463 463
464err_nomem: 464err_nomem:
465 kfree(node); 465 kfree(node);
466 pr_warning("out of memory\n"); 466 pr_warn("out of memory\n");
467 return NULL; 467 return NULL;
468} 468}
469 469
@@ -576,7 +576,7 @@ static void add_node(struct gcov_info *info)
576 struct gcov_node *parent; 576 struct gcov_node *parent;
577 struct gcov_node *node; 577 struct gcov_node *node;
578 578
579 filename = kstrdup(info->filename, GFP_KERNEL); 579 filename = kstrdup(gcov_info_filename(info), GFP_KERNEL);
580 if (!filename) 580 if (!filename)
581 return; 581 return;
582 parent = &root_node; 582 parent = &root_node;
@@ -630,8 +630,8 @@ static void add_info(struct gcov_node *node, struct gcov_info *info)
630 */ 630 */
631 loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL); 631 loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL);
632 if (!loaded_info) { 632 if (!loaded_info) {
633 pr_warning("could not add '%s' (out of memory)\n", 633 pr_warn("could not add '%s' (out of memory)\n",
634 info->filename); 634 gcov_info_filename(info));
635 return; 635 return;
636 } 636 }
637 memcpy(loaded_info, node->loaded_info, 637 memcpy(loaded_info, node->loaded_info,
@@ -644,8 +644,9 @@ static void add_info(struct gcov_node *node, struct gcov_info *info)
644 * data set replaces the copy of the last one. 644 * data set replaces the copy of the last one.
645 */ 645 */
646 if (!gcov_info_is_compatible(node->unloaded_info, info)) { 646 if (!gcov_info_is_compatible(node->unloaded_info, info)) {
647 pr_warning("discarding saved data for %s " 647 pr_warn("discarding saved data for %s "
648 "(incompatible version)\n", info->filename); 648 "(incompatible version)\n",
649 gcov_info_filename(info));
649 gcov_info_free(node->unloaded_info); 650 gcov_info_free(node->unloaded_info);
650 node->unloaded_info = NULL; 651 node->unloaded_info = NULL;
651 } 652 }
@@ -655,8 +656,8 @@ static void add_info(struct gcov_node *node, struct gcov_info *info)
655 * The initial one takes precedence. 656 * The initial one takes precedence.
656 */ 657 */
657 if (!gcov_info_is_compatible(node->loaded_info[0], info)) { 658 if (!gcov_info_is_compatible(node->loaded_info[0], info)) {
658 pr_warning("could not add '%s' (incompatible " 659 pr_warn("could not add '%s' (incompatible "
659 "version)\n", info->filename); 660 "version)\n", gcov_info_filename(info));
660 kfree(loaded_info); 661 kfree(loaded_info);
661 return; 662 return;
662 } 663 }
@@ -691,8 +692,9 @@ static void save_info(struct gcov_node *node, struct gcov_info *info)
691 else { 692 else {
692 node->unloaded_info = gcov_info_dup(info); 693 node->unloaded_info = gcov_info_dup(info);
693 if (!node->unloaded_info) { 694 if (!node->unloaded_info) {
694 pr_warning("could not save data for '%s' " 695 pr_warn("could not save data for '%s' "
695 "(out of memory)\n", info->filename); 696 "(out of memory)\n",
697 gcov_info_filename(info));
696 } 698 }
697 } 699 }
698} 700}
@@ -707,8 +709,8 @@ static void remove_info(struct gcov_node *node, struct gcov_info *info)
707 709
708 i = get_info_index(node, info); 710 i = get_info_index(node, info);
709 if (i < 0) { 711 if (i < 0) {
710 pr_warning("could not remove '%s' (not found)\n", 712 pr_warn("could not remove '%s' (not found)\n",
711 info->filename); 713 gcov_info_filename(info));
712 return; 714 return;
713 } 715 }
714 if (gcov_persist) 716 if (gcov_persist)
@@ -735,7 +737,7 @@ void gcov_event(enum gcov_action action, struct gcov_info *info)
735 struct gcov_node *node; 737 struct gcov_node *node;
736 738
737 mutex_lock(&node_lock); 739 mutex_lock(&node_lock);
738 node = get_node_by_name(info->filename); 740 node = get_node_by_name(gcov_info_filename(info));
739 switch (action) { 741 switch (action) {
740 case GCOV_ADD: 742 case GCOV_ADD:
741 if (node) 743 if (node)
@@ -747,8 +749,8 @@ void gcov_event(enum gcov_action action, struct gcov_info *info)
747 if (node) 749 if (node)
748 remove_info(node, info); 750 remove_info(node, info);
749 else { 751 else {
750 pr_warning("could not remove '%s' (not found)\n", 752 pr_warn("could not remove '%s' (not found)\n",
751 info->filename); 753 gcov_info_filename(info));
752 } 754 }
753 break; 755 break;
754 } 756 }
diff --git a/kernel/gcov/gcc_3_4.c b/kernel/gcov/gcc_3_4.c
index ae5bb4260033..27bc88a35013 100644
--- a/kernel/gcov/gcc_3_4.c
+++ b/kernel/gcov/gcc_3_4.c
@@ -21,6 +21,121 @@
21#include <linux/vmalloc.h> 21#include <linux/vmalloc.h>
22#include "gcov.h" 22#include "gcov.h"
23 23
24#define GCOV_COUNTERS 5
25
26static struct gcov_info *gcov_info_head;
27
28/**
29 * struct gcov_fn_info - profiling meta data per function
30 * @ident: object file-unique function identifier
31 * @checksum: function checksum
32 * @n_ctrs: number of values per counter type belonging to this function
33 *
34 * This data is generated by gcc during compilation and doesn't change
35 * at run-time.
36 */
37struct gcov_fn_info {
38 unsigned int ident;
39 unsigned int checksum;
40 unsigned int n_ctrs[0];
41};
42
43/**
44 * struct gcov_ctr_info - profiling data per counter type
45 * @num: number of counter values for this type
46 * @values: array of counter values for this type
47 * @merge: merge function for counter values of this type (unused)
48 *
49 * This data is generated by gcc during compilation and doesn't change
50 * at run-time with the exception of the values array.
51 */
52struct gcov_ctr_info {
53 unsigned int num;
54 gcov_type *values;
55 void (*merge)(gcov_type *, unsigned int);
56};
57
58/**
59 * struct gcov_info - profiling data per object file
60 * @version: gcov version magic indicating the gcc version used for compilation
61 * @next: list head for a singly-linked list
62 * @stamp: time stamp
63 * @filename: name of the associated gcov data file
64 * @n_functions: number of instrumented functions
65 * @functions: function data
66 * @ctr_mask: mask specifying which counter types are active
67 * @counts: counter data per counter type
68 *
69 * This data is generated by gcc during compilation and doesn't change
70 * at run-time with the exception of the next pointer.
71 */
72struct gcov_info {
73 unsigned int version;
74 struct gcov_info *next;
75 unsigned int stamp;
76 const char *filename;
77 unsigned int n_functions;
78 const struct gcov_fn_info *functions;
79 unsigned int ctr_mask;
80 struct gcov_ctr_info counts[0];
81};
82
83/**
84 * gcov_info_filename - return info filename
85 * @info: profiling data set
86 */
87const char *gcov_info_filename(struct gcov_info *info)
88{
89 return info->filename;
90}
91
92/**
93 * gcov_info_version - return info version
94 * @info: profiling data set
95 */
96unsigned int gcov_info_version(struct gcov_info *info)
97{
98 return info->version;
99}
100
101/**
102 * gcov_info_next - return next profiling data set
103 * @info: profiling data set
104 *
105 * Returns next gcov_info following @info or first gcov_info in the chain if
106 * @info is %NULL.
107 */
108struct gcov_info *gcov_info_next(struct gcov_info *info)
109{
110 if (!info)
111 return gcov_info_head;
112
113 return info->next;
114}
115
116/**
117 * gcov_info_link - link/add profiling data set to the list
118 * @info: profiling data set
119 */
120void gcov_info_link(struct gcov_info *info)
121{
122 info->next = gcov_info_head;
123 gcov_info_head = info;
124}
125
126/**
127 * gcov_info_unlink - unlink/remove profiling data set from the list
128 * @prev: previous profiling data set
129 * @info: profiling data set
130 */
131void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
132{
133 if (prev)
134 prev->next = info->next;
135 else
136 gcov_info_head = info->next;
137}
138
24/* Symbolic links to be created for each profiling data file. */ 139/* Symbolic links to be created for each profiling data file. */
25const struct gcov_link gcov_link[] = { 140const struct gcov_link gcov_link[] = {
26 { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */ 141 { 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
new file mode 100644
index 000000000000..2c6e4631c814
--- /dev/null
+++ b/kernel/gcov/gcc_4_7.c
@@ -0,0 +1,560 @@
1/*
2 * This code provides functions to handle gcc's profiling data format
3 * introduced with gcc 4.7.
4 *
5 * This file is based heavily on gcc_3_4.c file.
6 *
7 * For a better understanding, refer to gcc source:
8 * gcc/gcov-io.h
9 * libgcc/libgcov.c
10 *
11 * Uses gcc-internal data definitions.
12 */
13
14#include <linux/errno.h>
15#include <linux/slab.h>
16#include <linux/string.h>
17#include <linux/seq_file.h>
18#include <linux/vmalloc.h>
19#include "gcov.h"
20
21#define GCOV_COUNTERS 8
22#define GCOV_TAG_FUNCTION_LENGTH 3
23
24static struct gcov_info *gcov_info_head;
25
26/**
27 * struct gcov_ctr_info - information about counters for a single function
28 * @num: number of counter values for this type
29 * @values: array of counter values for this type
30 *
31 * This data is generated by gcc during compilation and doesn't change
32 * at run-time with the exception of the values array.
33 */
34struct gcov_ctr_info {
35 unsigned int num;
36 gcov_type *values;
37};
38
39/**
40 * struct gcov_fn_info - profiling meta data per function
41 * @key: comdat key
42 * @ident: unique ident of function
43 * @lineno_checksum: function lineo_checksum
44 * @cfg_checksum: function cfg checksum
45 * @ctrs: instrumented counters
46 *
47 * This data is generated by gcc during compilation and doesn't change
48 * at run-time.
49 *
50 * Information about a single function. This uses the trailing array
51 * idiom. The number of counters is determined from the merge pointer
52 * array in gcov_info. The key is used to detect which of a set of
53 * comdat functions was selected -- it points to the gcov_info object
54 * of the object file containing the selected comdat function.
55 */
56struct gcov_fn_info {
57 const struct gcov_info *key;
58 unsigned int ident;
59 unsigned int lineno_checksum;
60 unsigned int cfg_checksum;
61 struct gcov_ctr_info ctrs[0];
62};
63
64/**
65 * struct gcov_info - profiling data per object file
66 * @version: gcov version magic indicating the gcc version used for compilation
67 * @next: list head for a singly-linked list
68 * @stamp: uniquifying time stamp
69 * @filename: name of the associated gcov data file
70 * @merge: merge functions (null for unused counter type)
71 * @n_functions: number of instrumented functions
72 * @functions: pointer to pointers to function information
73 *
74 * This data is generated by gcc during compilation and doesn't change
75 * at run-time with the exception of the next pointer.
76 */
77struct gcov_info {
78 unsigned int version;
79 struct gcov_info *next;
80 unsigned int stamp;
81 const char *filename;
82 void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
83 unsigned int n_functions;
84 struct gcov_fn_info **functions;
85};
86
87/**
88 * gcov_info_filename - return info filename
89 * @info: profiling data set
90 */
91const char *gcov_info_filename(struct gcov_info *info)
92{
93 return info->filename;
94}
95
96/**
97 * gcov_info_version - return info version
98 * @info: profiling data set
99 */
100unsigned int gcov_info_version(struct gcov_info *info)
101{
102 return info->version;
103}
104
105/**
106 * gcov_info_next - return next profiling data set
107 * @info: profiling data set
108 *
109 * Returns next gcov_info following @info or first gcov_info in the chain if
110 * @info is %NULL.
111 */
112struct gcov_info *gcov_info_next(struct gcov_info *info)
113{
114 if (!info)
115 return gcov_info_head;
116
117 return info->next;
118}
119
120/**
121 * gcov_info_link - link/add profiling data set to the list
122 * @info: profiling data set
123 */
124void gcov_info_link(struct gcov_info *info)
125{
126 info->next = gcov_info_head;
127 gcov_info_head = info;
128}
129
130/**
131 * gcov_info_unlink - unlink/remove profiling data set from the list
132 * @prev: previous profiling data set
133 * @info: profiling data set
134 */
135void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
136{
137 if (prev)
138 prev->next = info->next;
139 else
140 gcov_info_head = info->next;
141}
142
143/* Symbolic links to be created for each profiling data file. */
144const struct gcov_link gcov_link[] = {
145 { OBJ_TREE, "gcno" }, /* Link to .gcno file in $(objtree). */
146 { 0, NULL},
147};
148
149/*
150 * Determine whether a counter is active. Doesn't change at run-time.
151 */
152static int counter_active(struct gcov_info *info, unsigned int type)
153{
154 return info->merge[type] ? 1 : 0;
155}
156
157/* Determine number of active counters. Based on gcc magic. */
158static unsigned int num_counter_active(struct gcov_info *info)
159{
160 unsigned int i;
161 unsigned int result = 0;
162
163 for (i = 0; i < GCOV_COUNTERS; i++) {
164 if (counter_active(info, i))
165 result++;
166 }
167 return result;
168}
169
170/**
171 * gcov_info_reset - reset profiling data to zero
172 * @info: profiling data set
173 */
174void gcov_info_reset(struct gcov_info *info)
175{
176 struct gcov_ctr_info *ci_ptr;
177 unsigned int fi_idx;
178 unsigned int ct_idx;
179
180 for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
181 ci_ptr = info->functions[fi_idx]->ctrs;
182
183 for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
184 if (!counter_active(info, ct_idx))
185 continue;
186
187 memset(ci_ptr->values, 0,
188 sizeof(gcov_type) * ci_ptr->num);
189 ci_ptr++;
190 }
191 }
192}
193
194/**
195 * gcov_info_is_compatible - check if profiling data can be added
196 * @info1: first profiling data set
197 * @info2: second profiling data set
198 *
199 * Returns non-zero if profiling data can be added, zero otherwise.
200 */
201int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
202{
203 return (info1->stamp == info2->stamp);
204}
205
206/**
207 * gcov_info_add - add up profiling data
208 * @dest: profiling data set to which data is added
209 * @source: profiling data set which is added
210 *
211 * Adds profiling counts of @source to @dest.
212 */
213void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
214{
215 struct gcov_ctr_info *dci_ptr;
216 struct gcov_ctr_info *sci_ptr;
217 unsigned int fi_idx;
218 unsigned int ct_idx;
219 unsigned int val_idx;
220
221 for (fi_idx = 0; fi_idx < src->n_functions; fi_idx++) {
222 dci_ptr = dst->functions[fi_idx]->ctrs;
223 sci_ptr = src->functions[fi_idx]->ctrs;
224
225 for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
226 if (!counter_active(src, ct_idx))
227 continue;
228
229 for (val_idx = 0; val_idx < sci_ptr->num; val_idx++)
230 dci_ptr->values[val_idx] +=
231 sci_ptr->values[val_idx];
232
233 dci_ptr++;
234 sci_ptr++;
235 }
236 }
237}
238
239/**
240 * gcov_info_dup - duplicate profiling data set
241 * @info: profiling data set to duplicate
242 *
243 * Return newly allocated duplicate on success, %NULL on error.
244 */
245struct gcov_info *gcov_info_dup(struct gcov_info *info)
246{
247 struct gcov_info *dup;
248 struct gcov_ctr_info *dci_ptr; /* dst counter info */
249 struct gcov_ctr_info *sci_ptr; /* src counter info */
250 unsigned int active;
251 unsigned int fi_idx; /* function info idx */
252 unsigned int ct_idx; /* counter type idx */
253 size_t fi_size; /* function info size */
254 size_t cv_size; /* counter values size */
255
256 dup = kmemdup(info, sizeof(*dup), GFP_KERNEL);
257 if (!dup)
258 return NULL;
259
260 dup->next = NULL;
261 dup->filename = NULL;
262 dup->functions = NULL;
263
264 dup->filename = kstrdup(info->filename, GFP_KERNEL);
265 if (!dup->filename)
266 goto err_free;
267
268 dup->functions = kcalloc(info->n_functions,
269 sizeof(struct gcov_fn_info *), GFP_KERNEL);
270 if (!dup->functions)
271 goto err_free;
272
273 active = num_counter_active(info);
274 fi_size = sizeof(struct gcov_fn_info);
275 fi_size += sizeof(struct gcov_ctr_info) * active;
276
277 for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
278 dup->functions[fi_idx] = kzalloc(fi_size, GFP_KERNEL);
279 if (!dup->functions[fi_idx])
280 goto err_free;
281
282 *(dup->functions[fi_idx]) = *(info->functions[fi_idx]);
283
284 sci_ptr = info->functions[fi_idx]->ctrs;
285 dci_ptr = dup->functions[fi_idx]->ctrs;
286
287 for (ct_idx = 0; ct_idx < active; ct_idx++) {
288
289 cv_size = sizeof(gcov_type) * sci_ptr->num;
290
291 dci_ptr->values = vmalloc(cv_size);
292
293 if (!dci_ptr->values)
294 goto err_free;
295
296 dci_ptr->num = sci_ptr->num;
297 memcpy(dci_ptr->values, sci_ptr->values, cv_size);
298
299 sci_ptr++;
300 dci_ptr++;
301 }
302 }
303
304 return dup;
305err_free:
306 gcov_info_free(dup);
307 return NULL;
308}
309
310/**
311 * gcov_info_free - release memory for profiling data set duplicate
312 * @info: profiling data set duplicate to free
313 */
314void gcov_info_free(struct gcov_info *info)
315{
316 unsigned int active;
317 unsigned int fi_idx;
318 unsigned int ct_idx;
319 struct gcov_ctr_info *ci_ptr;
320
321 if (!info->functions)
322 goto free_info;
323
324 active = num_counter_active(info);
325
326 for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
327 if (!info->functions[fi_idx])
328 continue;
329
330 ci_ptr = info->functions[fi_idx]->ctrs;
331
332 for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++)
333 vfree(ci_ptr->values);
334
335 kfree(info->functions[fi_idx]);
336 }
337
338free_info:
339 kfree(info->functions);
340 kfree(info->filename);
341 kfree(info);
342}
343
344#define ITER_STRIDE PAGE_SIZE
345
346/**
347 * struct gcov_iterator - specifies current file position in logical records
348 * @info: associated profiling data
349 * @buffer: buffer containing file data
350 * @size: size of buffer
351 * @pos: current position in file
352 */
353struct gcov_iterator {
354 struct gcov_info *info;
355 void *buffer;
356 size_t size;
357 loff_t pos;
358};
359
360/**
361 * store_gcov_u32 - store 32 bit number in gcov format to buffer
362 * @buffer: target buffer or NULL
363 * @off: offset into the buffer
364 * @v: value to be stored
365 *
366 * Number format defined by gcc: numbers are recorded in the 32 bit
367 * unsigned binary form of the endianness of the machine generating the
368 * file. Returns the number of bytes stored. If @buffer is %NULL, doesn't
369 * store anything.
370 */
371static size_t store_gcov_u32(void *buffer, size_t off, u32 v)
372{
373 u32 *data;
374
375 if (buffer) {
376 data = buffer + off;
377 *data = v;
378 }
379
380 return sizeof(*data);
381}
382
383/**
384 * store_gcov_u64 - store 64 bit number in gcov format to buffer
385 * @buffer: target buffer or NULL
386 * @off: offset into the buffer
387 * @v: value to be stored
388 *
389 * Number format defined by gcc: numbers are recorded in the 32 bit
390 * unsigned binary form of the endianness of the machine generating the
391 * file. 64 bit numbers are stored as two 32 bit numbers, the low part
392 * first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store
393 * anything.
394 */
395static size_t store_gcov_u64(void *buffer, size_t off, u64 v)
396{
397 u32 *data;
398
399 if (buffer) {
400 data = buffer + off;
401
402 data[0] = (v & 0xffffffffUL);
403 data[1] = (v >> 32);
404 }
405
406 return sizeof(*data) * 2;
407}
408
409/**
410 * convert_to_gcda - convert profiling data set to gcda file format
411 * @buffer: the buffer to store file data or %NULL if no data should be stored
412 * @info: profiling data set to be converted
413 *
414 * Returns the number of bytes that were/would have been stored into the buffer.
415 */
416static size_t convert_to_gcda(char *buffer, struct gcov_info *info)
417{
418 struct gcov_fn_info *fi_ptr;
419 struct gcov_ctr_info *ci_ptr;
420 unsigned int fi_idx;
421 unsigned int ct_idx;
422 unsigned int cv_idx;
423 size_t pos = 0;
424
425 /* File header. */
426 pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
427 pos += store_gcov_u32(buffer, pos, info->version);
428 pos += store_gcov_u32(buffer, pos, info->stamp);
429
430 for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
431 fi_ptr = info->functions[fi_idx];
432
433 /* Function record. */
434 pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
435 pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH);
436 pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
437 pos += store_gcov_u32(buffer, pos, fi_ptr->lineno_checksum);
438 pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum);
439
440 ci_ptr = fi_ptr->ctrs;
441
442 for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
443 if (!counter_active(info, ct_idx))
444 continue;
445
446 /* Counter record. */
447 pos += store_gcov_u32(buffer, pos,
448 GCOV_TAG_FOR_COUNTER(ct_idx));
449 pos += store_gcov_u32(buffer, pos, ci_ptr->num * 2);
450
451 for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
452 pos += store_gcov_u64(buffer, pos,
453 ci_ptr->values[cv_idx]);
454 }
455
456 ci_ptr++;
457 }
458 }
459
460 return pos;
461}
462
463/**
464 * gcov_iter_new - allocate and initialize profiling data iterator
465 * @info: profiling data set to be iterated
466 *
467 * Return file iterator on success, %NULL otherwise.
468 */
469struct gcov_iterator *gcov_iter_new(struct gcov_info *info)
470{
471 struct gcov_iterator *iter;
472
473 iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL);
474 if (!iter)
475 goto err_free;
476
477 iter->info = info;
478 /* Dry-run to get the actual buffer size. */
479 iter->size = convert_to_gcda(NULL, info);
480 iter->buffer = vmalloc(iter->size);
481 if (!iter->buffer)
482 goto err_free;
483
484 convert_to_gcda(iter->buffer, info);
485
486 return iter;
487
488err_free:
489 kfree(iter);
490 return NULL;
491}
492
493
494/**
495 * gcov_iter_get_info - return profiling data set for given file iterator
496 * @iter: file iterator
497 */
498void gcov_iter_free(struct gcov_iterator *iter)
499{
500 vfree(iter->buffer);
501 kfree(iter);
502}
503
504/**
505 * gcov_iter_get_info - return profiling data set for given file iterator
506 * @iter: file iterator
507 */
508struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter)
509{
510 return iter->info;
511}
512
513/**
514 * gcov_iter_start - reset file iterator to starting position
515 * @iter: file iterator
516 */
517void gcov_iter_start(struct gcov_iterator *iter)
518{
519 iter->pos = 0;
520}
521
522/**
523 * gcov_iter_next - advance file iterator to next logical record
524 * @iter: file iterator
525 *
526 * Return zero if new position is valid, non-zero if iterator has reached end.
527 */
528int gcov_iter_next(struct gcov_iterator *iter)
529{
530 if (iter->pos < iter->size)
531 iter->pos += ITER_STRIDE;
532
533 if (iter->pos >= iter->size)
534 return -EINVAL;
535
536 return 0;
537}
538
539/**
540 * gcov_iter_write - write data for current pos to seq_file
541 * @iter: file iterator
542 * @seq: seq_file handle
543 *
544 * Return zero on success, non-zero otherwise.
545 */
546int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq)
547{
548 size_t len;
549
550 if (iter->pos >= iter->size)
551 return -EINVAL;
552
553 len = ITER_STRIDE;
554 if (iter->pos + len > iter->size)
555 len = iter->size - iter->pos;
556
557 seq_write(seq, iter->buffer + iter->pos, len);
558
559 return 0;
560}
diff --git a/kernel/gcov/gcov.h b/kernel/gcov/gcov.h
index 060073ebf7a6..92c8e22a29ed 100644
--- a/kernel/gcov/gcov.h
+++ b/kernel/gcov/gcov.h
@@ -21,7 +21,6 @@
21 * gcc and need to be kept as close to the original definition as possible to 21 * gcc and need to be kept as close to the original definition as possible to
22 * remain compatible. 22 * remain compatible.
23 */ 23 */
24#define GCOV_COUNTERS 5
25#define GCOV_DATA_MAGIC ((unsigned int) 0x67636461) 24#define GCOV_DATA_MAGIC ((unsigned int) 0x67636461)
26#define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000) 25#define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000)
27#define GCOV_TAG_COUNTER_BASE ((unsigned int) 0x01a10000) 26#define GCOV_TAG_COUNTER_BASE ((unsigned int) 0x01a10000)
@@ -34,60 +33,18 @@ typedef long gcov_type;
34typedef long long gcov_type; 33typedef long long gcov_type;
35#endif 34#endif
36 35
37/** 36/* Opaque gcov_info. The gcov structures can change as for example in gcc 4.7 so
38 * struct gcov_fn_info - profiling meta data per function 37 * we cannot use full definition here and they need to be placed in gcc specific
39 * @ident: object file-unique function identifier 38 * implementation of gcov. This also means no direct access to the members in
40 * @checksum: function checksum 39 * generic code and usage of the interface below.*/
41 * @n_ctrs: number of values per counter type belonging to this function 40struct gcov_info;
42 *
43 * This data is generated by gcc during compilation and doesn't change
44 * at run-time.
45 */
46struct gcov_fn_info {
47 unsigned int ident;
48 unsigned int checksum;
49 unsigned int n_ctrs[0];
50};
51
52/**
53 * struct gcov_ctr_info - profiling data per counter type
54 * @num: number of counter values for this type
55 * @values: array of counter values for this type
56 * @merge: merge function for counter values of this type (unused)
57 *
58 * This data is generated by gcc during compilation and doesn't change
59 * at run-time with the exception of the values array.
60 */
61struct gcov_ctr_info {
62 unsigned int num;
63 gcov_type *values;
64 void (*merge)(gcov_type *, unsigned int);
65};
66 41
67/** 42/* Interface to access gcov_info data */
68 * struct gcov_info - profiling data per object file 43const char *gcov_info_filename(struct gcov_info *info);
69 * @version: gcov version magic indicating the gcc version used for compilation 44unsigned int gcov_info_version(struct gcov_info *info);
70 * @next: list head for a singly-linked list 45struct gcov_info *gcov_info_next(struct gcov_info *info);
71 * @stamp: time stamp 46void gcov_info_link(struct gcov_info *info);
72 * @filename: name of the associated gcov data file 47void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info);
73 * @n_functions: number of instrumented functions
74 * @functions: function data
75 * @ctr_mask: mask specifying which counter types are active
76 * @counts: counter data per counter type
77 *
78 * This data is generated by gcc during compilation and doesn't change
79 * at run-time with the exception of the next pointer.
80 */
81struct gcov_info {
82 unsigned int version;
83 struct gcov_info *next;
84 unsigned int stamp;
85 const char *filename;
86 unsigned int n_functions;
87 const struct gcov_fn_info *functions;
88 unsigned int ctr_mask;
89 struct gcov_ctr_info counts[0];
90};
91 48
92/* Base interface. */ 49/* Base interface. */
93enum gcov_action { 50enum gcov_action {