ftrace: add self-tests

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 9bad237..f6d026f 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -32,6 +32,8 @@
 unsigned long __read_mostly	tracing_max_latency = (cycle_t)ULONG_MAX;
 unsigned long __read_mostly	tracing_thresh;
 
+static int tracing_disabled = 1;
+
 static long notrace
 ns2usecs(cycle_t nsec)
 {
@@ -217,11 +219,48 @@
 		}
 	}
 
+#ifdef CONFIG_FTRACE_STARTUP_TEST
+	if (type->selftest) {
+		struct tracer *saved_tracer = current_trace;
+		struct trace_array_cpu *data;
+		struct trace_array *tr = &global_trace;
+		int saved_ctrl = tr->ctrl;
+		int i;
+		/*
+		 * Run a selftest on this tracer.
+		 * Here we reset the trace buffer, and set the current
+		 * tracer to be this tracer. The tracer can then run some
+		 * internal tracing to verify that everything is in order.
+		 * If we fail, we do not register this tracer.
+		 */
+		for_each_possible_cpu(i) {
+			if (!data->trace)
+				continue;
+			data = tr->data[i];
+			tracing_reset(data);
+		}
+		current_trace = type;
+		tr->ctrl = 0;
+		/* the test is responsible for initializing and enabling */
+		pr_info("Testing tracer %s: ", type->name);
+		ret = type->selftest(type, tr);
+		/* the test is responsible for resetting too */
+		current_trace = saved_tracer;
+		tr->ctrl = saved_ctrl;
+		if (ret) {
+			printk(KERN_CONT "FAILED!\n");
+			goto out;
+		}
+		printk(KERN_CONT "PASSED\n");
+	}
+#endif
+
 	type->next = trace_types;
 	trace_types = type;
 	len = strlen(type->name);
 	if (len > max_tracer_type_len)
 		max_tracer_type_len = len;
+
  out:
 	mutex_unlock(&trace_types_lock);
 
@@ -985,6 +1024,11 @@
 {
 	struct trace_iterator *iter;
 
+	if (tracing_disabled) {
+		*ret = -ENODEV;
+		return NULL;
+	}
+
 	iter = kzalloc(sizeof(*iter), GFP_KERNEL);
 	if (!iter) {
 		*ret = -ENOMEM;
@@ -1023,6 +1067,9 @@
 
 int tracing_open_generic(struct inode *inode, struct file *filp)
 {
+	if (tracing_disabled)
+		return -ENODEV;
+
 	filp->private_data = inode->i_private;
 	return 0;
 }
@@ -1128,6 +1175,9 @@
 {
 	int ret;
 
+	if (tracing_disabled)
+		return -ENODEV;
+
 	ret = seq_open(file, &show_traces_seq_ops);
 	if (!ret) {
 		struct seq_file *m = file->private_data;
@@ -1452,6 +1502,11 @@
 	return d_tracer;
 }
 
+#ifdef CONFIG_FTRACE_SELFTEST
+/* Let selftest have access to static functions in this file */
+#include "trace_selftest.c"
+#endif
+
 static __init void tracer_init_debugfs(void)
 {
 	struct dentry *d_tracer;
@@ -1585,6 +1640,7 @@
 	void *array;
 	struct page *page;
 	int pages = 0;
+	int ret = -ENOMEM;
 	int i;
 
 	/* Allocate the first page for all buffers */
@@ -1650,6 +1706,9 @@
 	register_tracer(&no_tracer);
 	current_trace = &no_tracer;
 
+	/* All seems OK, enable tracing */
+	tracing_disabled = 0;
+
 	return 0;
 
  free_buffers:
@@ -1678,7 +1737,7 @@
 		}
 #endif
 	}
-	return -ENOMEM;
+	return ret;
 }
 
-device_initcall(tracer_alloc_buffers);
+fs_initcall(tracer_alloc_buffers);