diff options
| -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 { |
