diff options
author | Frantisek Hrbata <fhrbata@redhat.com> | 2013-11-12 18:11:24 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-12 22:09:34 -0500 |
commit | 8cbce376e3fdf4a21f59365aefbb52eac3c2e312 (patch) | |
tree | 297fd82c41be44d87e1f2f9ea2302b6243c1e7f4 /kernel/gcov | |
parent | 0d20633b041041ecda39ae562e62087acf0092f1 (diff) |
gcov: move gcov structs definitions to a gcc version specific file
Since also the gcov structures(gcov_info, gcov_fn_info, gcov_ctr_info) can
change between gcc releases, as shown in gcc 4.7, they cannot be defined
in a common header and need to be moved to a specific gcc implemention
file. This also requires to make the gcov_info structure opaque for the
common code and to introduce simple helpers for accessing data inside
gcov_info.
Signed-off-by: Frantisek Hrbata <fhrbata@redhat.com>
Cc: Jan Stancek <jstancek@redhat.com>
Cc: Kees Cook <keescook@chromium.org>
Acked-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Andy Gospodarek <agospoda@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/gcov')
-rw-r--r-- | kernel/gcov/base.c | 26 | ||||
-rw-r--r-- | kernel/gcov/fs.c | 27 | ||||
-rw-r--r-- | kernel/gcov/gcc_3_4.c | 115 | ||||
-rw-r--r-- | kernel/gcov/gcov.h | 65 |
4 files changed, 153 insertions, 80 deletions
diff --git a/kernel/gcov/base.c b/kernel/gcov/base.c index 9b22d03cc581..912576a671d8 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 | ||
23 | static struct gcov_info *gcov_info_head; | ||
24 | static int gcov_events_enabled; | 23 | static int gcov_events_enabled; |
25 | static DEFINE_MUTEX(gcov_lock); | 24 | static 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); |
@@ -91,13 +89,15 @@ EXPORT_SYMBOL(__gcov_merge_delta); | |||
91 | */ | 89 | */ |
92 | void gcov_enable_events(void) | 90 | void gcov_enable_events(void) |
93 | { | 91 | { |
94 | struct gcov_info *info; | 92 | struct gcov_info *info = NULL; |
95 | 93 | ||
96 | mutex_lock(&gcov_lock); | 94 | mutex_lock(&gcov_lock); |
97 | gcov_events_enabled = 1; | 95 | gcov_events_enabled = 1; |
96 | |||
98 | /* Perform event callback for previously registered entries. */ | 97 | /* Perform event callback for previously registered entries. */ |
99 | for (info = gcov_info_head; info; info = info->next) | 98 | while ((info = gcov_info_next(info))) |
100 | gcov_event(GCOV_ADD, info); | 99 | gcov_event(GCOV_ADD, info); |
100 | |||
101 | mutex_unlock(&gcov_lock); | 101 | mutex_unlock(&gcov_lock); |
102 | } | 102 | } |
103 | 103 | ||
@@ -112,25 +112,23 @@ static int gcov_module_notifier(struct notifier_block *nb, unsigned long event, | |||
112 | void *data) | 112 | void *data) |
113 | { | 113 | { |
114 | struct module *mod = data; | 114 | struct module *mod = data; |
115 | struct gcov_info *info; | 115 | struct gcov_info *info = NULL; |
116 | struct gcov_info *prev; | 116 | struct gcov_info *prev = NULL; |
117 | 117 | ||
118 | if (event != MODULE_STATE_GOING) | 118 | if (event != MODULE_STATE_GOING) |
119 | return NOTIFY_OK; | 119 | return NOTIFY_OK; |
120 | mutex_lock(&gcov_lock); | 120 | mutex_lock(&gcov_lock); |
121 | prev = NULL; | 121 | |
122 | /* Remove entries located in module from linked list. */ | 122 | /* Remove entries located in module from linked list. */ |
123 | for (info = gcov_info_head; info; info = info->next) { | 123 | while ((info = gcov_info_next(info))) { |
124 | if (within(info, mod->module_core, mod->core_size)) { | 124 | if (within(info, mod->module_core, mod->core_size)) { |
125 | if (prev) | 125 | gcov_info_unlink(prev, info); |
126 | prev->next = info->next; | ||
127 | else | ||
128 | gcov_info_head = info->next; | ||
129 | if (gcov_events_enabled) | 126 | if (gcov_events_enabled) |
130 | gcov_event(GCOV_REMOVE, info); | 127 | gcov_event(GCOV_REMOVE, info); |
131 | } else | 128 | } else |
132 | prev = info; | 129 | prev = info; |
133 | } | 130 | } |
131 | |||
134 | mutex_unlock(&gcov_lock); | 132 | mutex_unlock(&gcov_lock); |
135 | 133 | ||
136 | return NOTIFY_OK; | 134 | return NOTIFY_OK; |
diff --git a/kernel/gcov/fs.c b/kernel/gcov/fs.c index 7a7d2ee96d42..b49dfce8e8a1 100644 --- a/kernel/gcov/fs.c +++ b/kernel/gcov/fs.c | |||
@@ -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) |
@@ -376,8 +376,9 @@ 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 = strrchr(target, '/'); |
@@ -576,7 +577,7 @@ static void add_node(struct gcov_info *info) | |||
576 | struct gcov_node *parent; | 577 | struct gcov_node *parent; |
577 | struct gcov_node *node; | 578 | struct gcov_node *node; |
578 | 579 | ||
579 | filename = kstrdup(info->filename, GFP_KERNEL); | 580 | filename = kstrdup(gcov_info_filename(info), GFP_KERNEL); |
580 | if (!filename) | 581 | if (!filename) |
581 | return; | 582 | return; |
582 | parent = &root_node; | 583 | parent = &root_node; |
@@ -631,7 +632,7 @@ static void add_info(struct gcov_node *node, struct gcov_info *info) | |||
631 | loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL); | 632 | loaded_info = kcalloc(num + 1, sizeof(struct gcov_info *), GFP_KERNEL); |
632 | if (!loaded_info) { | 633 | if (!loaded_info) { |
633 | pr_warning("could not add '%s' (out of memory)\n", | 634 | pr_warning("could not add '%s' (out of memory)\n", |
634 | info->filename); | 635 | gcov_info_filename(info)); |
635 | return; | 636 | return; |
636 | } | 637 | } |
637 | memcpy(loaded_info, node->loaded_info, | 638 | memcpy(loaded_info, node->loaded_info, |
@@ -645,7 +646,8 @@ static void add_info(struct gcov_node *node, struct gcov_info *info) | |||
645 | */ | 646 | */ |
646 | if (!gcov_info_is_compatible(node->unloaded_info, info)) { | 647 | if (!gcov_info_is_compatible(node->unloaded_info, info)) { |
647 | pr_warning("discarding saved data for %s " | 648 | pr_warning("discarding saved data for %s " |
648 | "(incompatible version)\n", info->filename); | 649 | "(incompatible version)\n", |
650 | gcov_info_filename(info)); | ||
649 | gcov_info_free(node->unloaded_info); | 651 | gcov_info_free(node->unloaded_info); |
650 | node->unloaded_info = NULL; | 652 | node->unloaded_info = NULL; |
651 | } | 653 | } |
@@ -656,7 +658,7 @@ static void add_info(struct gcov_node *node, struct gcov_info *info) | |||
656 | */ | 658 | */ |
657 | if (!gcov_info_is_compatible(node->loaded_info[0], info)) { | 659 | if (!gcov_info_is_compatible(node->loaded_info[0], info)) { |
658 | pr_warning("could not add '%s' (incompatible " | 660 | pr_warning("could not add '%s' (incompatible " |
659 | "version)\n", info->filename); | 661 | "version)\n", gcov_info_filename(info)); |
660 | kfree(loaded_info); | 662 | kfree(loaded_info); |
661 | return; | 663 | return; |
662 | } | 664 | } |
@@ -692,7 +694,8 @@ static void save_info(struct gcov_node *node, struct gcov_info *info) | |||
692 | node->unloaded_info = gcov_info_dup(info); | 694 | node->unloaded_info = gcov_info_dup(info); |
693 | if (!node->unloaded_info) { | 695 | if (!node->unloaded_info) { |
694 | pr_warning("could not save data for '%s' " | 696 | pr_warning("could not save data for '%s' " |
695 | "(out of memory)\n", info->filename); | 697 | "(out of memory)\n", |
698 | gcov_info_filename(info)); | ||
696 | } | 699 | } |
697 | } | 700 | } |
698 | } | 701 | } |
@@ -708,7 +711,7 @@ static void remove_info(struct gcov_node *node, struct gcov_info *info) | |||
708 | i = get_info_index(node, info); | 711 | i = get_info_index(node, info); |
709 | if (i < 0) { | 712 | if (i < 0) { |
710 | pr_warning("could not remove '%s' (not found)\n", | 713 | pr_warning("could not remove '%s' (not found)\n", |
711 | info->filename); | 714 | gcov_info_filename(info)); |
712 | return; | 715 | return; |
713 | } | 716 | } |
714 | if (gcov_persist) | 717 | if (gcov_persist) |
@@ -735,7 +738,7 @@ void gcov_event(enum gcov_action action, struct gcov_info *info) | |||
735 | struct gcov_node *node; | 738 | struct gcov_node *node; |
736 | 739 | ||
737 | mutex_lock(&node_lock); | 740 | mutex_lock(&node_lock); |
738 | node = get_node_by_name(info->filename); | 741 | node = get_node_by_name(gcov_info_filename(info)); |
739 | switch (action) { | 742 | switch (action) { |
740 | case GCOV_ADD: | 743 | case GCOV_ADD: |
741 | if (node) | 744 | if (node) |
@@ -748,7 +751,7 @@ void gcov_event(enum gcov_action action, struct gcov_info *info) | |||
748 | remove_info(node, info); | 751 | remove_info(node, info); |
749 | else { | 752 | else { |
750 | pr_warning("could not remove '%s' (not found)\n", | 753 | pr_warning("could not remove '%s' (not found)\n", |
751 | info->filename); | 754 | gcov_info_filename(info)); |
752 | } | 755 | } |
753 | break; | 756 | break; |
754 | } | 757 | } |
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 | |||
26 | static 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 | */ | ||
37 | struct 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 | */ | ||
52 | struct 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 | */ | ||
72 | struct 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 | */ | ||
87 | const 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 | */ | ||
96 | unsigned 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 | */ | ||
108 | struct 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 | */ | ||
120 | void 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 | */ | ||
131 | void 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. */ |
25 | const struct gcov_link gcov_link[] = { | 140 | const 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/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; | |||
34 | typedef long long gcov_type; | 33 | typedef 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 | 40 | struct gcov_info; |
42 | * | ||
43 | * This data is generated by gcc during compilation and doesn't change | ||
44 | * at run-time. | ||
45 | */ | ||
46 | struct 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 | */ | ||
61 | struct 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 | 43 | const char *gcov_info_filename(struct gcov_info *info); |
69 | * @version: gcov version magic indicating the gcc version used for compilation | 44 | unsigned int gcov_info_version(struct gcov_info *info); |
70 | * @next: list head for a singly-linked list | 45 | struct gcov_info *gcov_info_next(struct gcov_info *info); |
71 | * @stamp: time stamp | 46 | void gcov_info_link(struct gcov_info *info); |
72 | * @filename: name of the associated gcov data file | 47 | void 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 | */ | ||
81 | struct 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. */ |
93 | enum gcov_action { | 50 | enum gcov_action { |