diff options
Diffstat (limited to 'kernel/gcov')
-rw-r--r-- | kernel/gcov/Kconfig | 30 | ||||
-rw-r--r-- | kernel/gcov/Makefile | 32 | ||||
-rw-r--r-- | kernel/gcov/base.c | 32 | ||||
-rw-r--r-- | kernel/gcov/fs.c | 52 | ||||
-rw-r--r-- | kernel/gcov/gcc_3_4.c | 115 | ||||
-rw-r--r-- | kernel/gcov/gcc_4_7.c | 560 | ||||
-rw-r--r-- | kernel/gcov/gcov.h | 65 |
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 | ||
49 | choice | ||
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 | |||
61 | config GCOV_FORMAT_AUTODETECT | ||
62 | bool "Autodetect" | ||
63 | ---help--- | ||
64 | Select this option to use the format that corresponds to your GCC | ||
65 | version. | ||
66 | |||
67 | config 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 | |||
72 | config 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 | |||
77 | endchoice | ||
78 | |||
49 | endmenu | 79 | endmenu |
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 @@ | |||
1 | ccflags-y := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"' | 1 | ccflags-y := -DSRCTREE='"$(srctree)"' -DOBJTREE='"$(objtree)"' |
2 | 2 | ||
3 | obj-$(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) | ||
6 | if-lt = $(shell [ $(1) -lt $(2) ] && echo 1) | ||
7 | |||
8 | ifeq ($(CONFIG_GCOV_FORMAT_3_4),y) | ||
9 | cc-ver := 0304 | ||
10 | else ifeq ($(CONFIG_GCOV_FORMAT_4_7),y) | ||
11 | cc-ver := 0407 | ||
12 | else | ||
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) | ||
25 | endif | ||
26 | |||
27 | obj-$(CONFIG_GCOV_KERNEL) := base.o fs.o | ||
28 | |||
29 | ifeq ($(call if-lt, $(cc-ver), 0407),1) | ||
30 | obj-$(CONFIG_GCOV_KERNEL) += gcc_3_4.o | ||
31 | else | ||
32 | obj-$(CONFIG_GCOV_KERNEL) += gcc_4_7.o | ||
33 | endif | ||
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 | ||
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); |
@@ -81,6 +79,12 @@ void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters) | |||
81 | } | 79 | } |
82 | EXPORT_SYMBOL(__gcov_merge_delta); | 80 | EXPORT_SYMBOL(__gcov_merge_delta); |
83 | 81 | ||
82 | void __gcov_merge_ior(gcov_type *counters, unsigned int n_counters) | ||
83 | { | ||
84 | /* Unused. */ | ||
85 | } | ||
86 | EXPORT_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 | */ |
92 | void gcov_enable_events(void) | 96 | void 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 | */ |
366 | static void add_links(struct gcov_node *node, struct dentry *parent) | 366 | static 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 | ||
464 | err_nomem: | 464 | err_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 | |||
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/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 | |||
24 | static 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 | */ | ||
34 | struct 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 | */ | ||
56 | struct 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 | */ | ||
77 | struct 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 | */ | ||
91 | const 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 | */ | ||
100 | unsigned 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 | */ | ||
112 | struct 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 | */ | ||
124 | void 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 | */ | ||
135 | void 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. */ | ||
144 | const 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 | */ | ||
152 | static 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. */ | ||
158 | static 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 | */ | ||
174 | void 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 | */ | ||
201 | int 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 | */ | ||
213 | void 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 | */ | ||
245 | struct 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; | ||
305 | err_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 | */ | ||
314 | void 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 | |||
338 | free_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 | */ | ||
353 | struct 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 | */ | ||
371 | static 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 | */ | ||
395 | static 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 | */ | ||
416 | static 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 | */ | ||
469 | struct 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 | |||
488 | err_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 | */ | ||
498 | void 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 | */ | ||
508 | struct 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 | */ | ||
517 | void 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 | */ | ||
528 | int 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 | */ | ||
546 | int 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; | |||
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 { |