]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/gcov/base.c
gcov: add gcov profiling infrastructure
[karo-tx-linux.git] / kernel / gcov / base.c
diff --git a/kernel/gcov/base.c b/kernel/gcov/base.c
new file mode 100644 (file)
index 0000000..9b22d03
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ *  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 "gcov.h"
+
+static struct gcov_info *gcov_info_head;
+static int gcov_events_enabled;
+static DEFINE_MUTEX(gcov_lock);
+
+/*
+ * __gcov_init is called by gcc-generated constructor code for each object
+ * file compiled with -fprofile-arcs.
+ */
+void __gcov_init(struct gcov_info *info)
+{
+       static unsigned int gcov_version;
+
+       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_event(GCOV_ADD, info);
+       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.
+ */
+void __gcov_flush(void)
+{
+       /* Unused. */
+}
+EXPORT_SYMBOL(__gcov_flush);
+
+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);
+
+void __gcov_merge_delta(gcov_type *counters, unsigned int n_counters)
+{
+       /* Unused. */
+}
+EXPORT_SYMBOL(__gcov_merge_delta);
+
+/**
+ * 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;
+
+       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);
+}
+
+#ifdef CONFIG_MODULES
+static inline int within(void *addr, void *start, unsigned long size)
+{
+       return ((addr >= start) && (addr < start + size));
+}
+
+/* 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;
+
+       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;
+                       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 */