diff options
Diffstat (limited to 'kernel/gcov/base.c')
| -rw-r--r-- | kernel/gcov/base.c | 136 |
1 files changed, 63 insertions, 73 deletions
diff --git a/kernel/gcov/base.c b/kernel/gcov/base.c index 9b22d03cc581..073a3738c5e6 100644 --- a/kernel/gcov/base.c +++ b/kernel/gcov/base.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * This code maintains a list of active profiling data structures. * @@ -18,119 +19,108 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/sched.h> #include "gcov.h" -static struct gcov_info *gcov_info_head; -static int gcov_events_enabled; -static DEFINE_MUTEX(gcov_lock); +int gcov_events_enabled; +DEFINE_MUTEX(gcov_lock); -/* - * __gcov_init is called by gcc-generated constructor code for each object - * file compiled with -fprofile-arcs. +/** + * gcov_enable_events - enable event reporting through gcov_event() + * + * Turn on reporting of profiling data load/unload-events through the + * gcov_event() callback. Also replay all previous events once. This function + * is needed because some events are potentially generated too early for the + * callback implementation to handle them initially. */ -void __gcov_init(struct gcov_info *info) +void gcov_enable_events(void) { - static unsigned int gcov_version; + struct gcov_info *info = NULL; mutex_lock(&gcov_lock); - if (gcov_version == 0) { - gcov_version = info->version; - /* - * Printing gcc's version magic may prove useful for debugging - * incompatibility reports. - */ - pr_info("version magic: 0x%x\n", gcov_version); - } - /* - * Add new profiling data structure to list and inform event - * listener. - */ - info->next = gcov_info_head; - gcov_info_head = info; - if (gcov_events_enabled) + gcov_events_enabled = 1; + + /* Perform event callback for previously registered entries. */ + while ((info = gcov_info_next(info))) { gcov_event(GCOV_ADD, info); + cond_resched(); + } + mutex_unlock(&gcov_lock); } -EXPORT_SYMBOL(__gcov_init); -/* - * These functions may be referenced by gcc-generated profiling code but serve - * no function for kernel profiling. +/** + * store_gcov_u32 - store 32 bit number in gcov format to buffer + * @buffer: target buffer or NULL + * @off: offset into the buffer + * @v: value to be stored + * + * Number format defined by gcc: numbers are recorded in the 32 bit + * unsigned binary form of the endianness of the machine generating the + * file. Returns the number of bytes stored. If @buffer is %NULL, doesn't + * store anything. */ -void __gcov_flush(void) +size_t store_gcov_u32(void *buffer, size_t off, u32 v) { - /* Unused. */ -} -EXPORT_SYMBOL(__gcov_flush); + u32 *data; -void __gcov_merge_add(gcov_type *counters, unsigned int n_counters) -{ - /* Unused. */ -} -EXPORT_SYMBOL(__gcov_merge_add); - -void __gcov_merge_single(gcov_type *counters, unsigned int n_counters) -{ - /* Unused. */ -} -EXPORT_SYMBOL(__gcov_merge_single); + if (buffer) { + data = buffer + off; + *data = v; + } -void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters) -{ - /* Unused. */ + return sizeof(*data); } -EXPORT_SYMBOL(__gcov_merge_delta); /** - * gcov_enable_events - enable event reporting through gcov_event() + * store_gcov_u64 - store 64 bit number in gcov format to buffer + * @buffer: target buffer or NULL + * @off: offset into the buffer + * @v: value to be stored * - * Turn on reporting of profiling data load/unload-events through the - * gcov_event() callback. Also replay all previous events once. This function - * is needed because some events are potentially generated too early for the - * callback implementation to handle them initially. + * Number format defined by gcc: numbers are recorded in the 32 bit + * unsigned binary form of the endianness of the machine generating the + * file. 64 bit numbers are stored as two 32 bit numbers, the low part + * first. Returns the number of bytes stored. If @buffer is %NULL, doesn't store + * anything. */ -void gcov_enable_events(void) +size_t store_gcov_u64(void *buffer, size_t off, u64 v) { - struct gcov_info *info; + u32 *data; - mutex_lock(&gcov_lock); - gcov_events_enabled = 1; - /* Perform event callback for previously registered entries. */ - for (info = gcov_info_head; info; info = info->next) - gcov_event(GCOV_ADD, info); - mutex_unlock(&gcov_lock); -} + if (buffer) { + data = buffer + off; -#ifdef CONFIG_MODULES -static inline int within(void *addr, void *start, unsigned long size) -{ - return ((addr >= start) && (addr < start + size)); + data[0] = (v & 0xffffffffUL); + data[1] = (v >> 32); + } + + return sizeof(*data) * 2; } +#ifdef CONFIG_MODULES /* Update list and generate events when modules are unloaded. */ static int gcov_module_notifier(struct notifier_block *nb, unsigned long event, void *data) { struct module *mod = data; - struct gcov_info *info; - struct gcov_info *prev; + struct gcov_info *info = NULL; + struct gcov_info *prev = NULL; if (event != MODULE_STATE_GOING) return NOTIFY_OK; mutex_lock(&gcov_lock); - prev = NULL; + /* Remove entries located in module from linked list. */ - for (info = gcov_info_head; info; info = info->next) { - if (within(info, mod->module_core, mod->core_size)) { - if (prev) - prev->next = info->next; - else - gcov_info_head = info->next; + while ((info = gcov_info_next(info))) { + if (gcov_info_within_module(info, mod)) { + gcov_info_unlink(prev, info); if (gcov_events_enabled) gcov_event(GCOV_REMOVE, info); } else prev = info; } + mutex_unlock(&gcov_lock); return NOTIFY_OK; |
