summaryrefslogtreecommitdiffstats
path: root/kernel/gcov/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/gcov/base.c')
-rw-r--r--kernel/gcov/base.c89
1 files changed, 89 insertions, 0 deletions
diff --git a/kernel/gcov/base.c b/kernel/gcov/base.c
new file mode 100644
index 000000000..0ffe9f194
--- /dev/null
+++ b/kernel/gcov/base.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This code maintains a list of active profiling data structures.
+ *
+ * Copyright IBM Corp. 2009
+ * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ *
+ * Uses gcc-internal data definitions.
+ * Based on the gcov-kernel patch by:
+ * Hubertus Franke <frankeh@us.ibm.com>
+ * Nigel Hinds <nhinds@us.ibm.com>
+ * Rajan Ravindran <rajancr@us.ibm.com>
+ * Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ * Paul Larson
+ */
+
+#define pr_fmt(fmt) "gcov: " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include "gcov.h"
+
+int gcov_events_enabled;
+DEFINE_MUTEX(gcov_lock);
+
+/**
+ * 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_enable_events(void)
+{
+ struct gcov_info *info = NULL;
+
+ mutex_lock(&gcov_lock);
+ 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);
+}
+
+#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 = NULL;
+ struct gcov_info *prev = NULL;
+
+ if (event != MODULE_STATE_GOING)
+ return NOTIFY_OK;
+ mutex_lock(&gcov_lock);
+
+ /* Remove entries located in module from linked list. */
+ 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;
+}
+
+static struct notifier_block gcov_nb = {
+ .notifier_call = gcov_module_notifier,
+};
+
+static int __init gcov_init(void)
+{
+ return register_module_notifier(&gcov_nb);
+}
+device_initcall(gcov_init);
+#endif /* CONFIG_MODULES */