summaryrefslogtreecommitdiff
path: root/kernel/gcov/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gcov/base.c')
-rw-r--r--kernel/gcov/base.c136
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;