ftrace: startup tester on dynamic tracing.
authorSteven Rostedt <srostedt@redhat.com>
Mon, 12 May 2008 19:20:45 +0000 (21:20 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 23 May 2008 18:41:06 +0000 (20:41 +0200)
This patch adds a startup self test on dynamic code modification
and filters. The test filters on a specific function, makes sure that
no other function is traced, exectutes the function, then makes sure that
the function is traced.

This patch also fixes a slight bug with the ftrace selftest, where
tracer_enabled was not being set.

Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
include/linux/ftrace.h
kernel/trace/ftrace.c
kernel/trace/trace_selftest.c

index 953a36d..a842d96 100644 (file)
@@ -55,6 +55,7 @@ struct dyn_ftrace {
 };
 
 int ftrace_force_update(void);
+void ftrace_set_filter(unsigned char *buf, int len, int reset);
 
 /* defined in arch */
 extern int ftrace_ip_converted(unsigned long ip);
@@ -70,6 +71,7 @@ extern void ftrace_call(void);
 extern void mcount_call(void);
 #else
 # define ftrace_force_update() ({ 0; })
+# define ftrace_set_filter(buf, len, reset) do { } while (0)
 #endif
 
 static inline void tracer_disable(void)
index 6d4d2e8..5e9389f 100644 (file)
@@ -1010,6 +1010,25 @@ ftrace_filter_write(struct file *file, const char __user *ubuf,
        return ret;
 }
 
+/**
+ * ftrace_set_filter - set a function to filter on in ftrace
+ * @buf - the string that holds the function filter text.
+ * @len - the length of the string.
+ * @reset - non zero to reset all filters before applying this filter.
+ *
+ * Filters denote which functions should be enabled when tracing is enabled.
+ * If @buf is NULL and reset is set, all functions will be enabled for tracing.
+ */
+notrace void ftrace_set_filter(unsigned char *buf, int len, int reset)
+{
+       mutex_lock(&ftrace_filter_lock);
+       if (reset)
+               ftrace_filter_reset();
+       if (buf)
+               ftrace_match(buf, len);
+       mutex_unlock(&ftrace_filter_lock);
+}
+
 static int notrace
 ftrace_filter_release(struct inode *inode, struct file *file)
 {
index c01874c..4c8a1b2 100644 (file)
@@ -99,6 +99,100 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count)
 }
 
 #ifdef CONFIG_FTRACE
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+#define DYN_FTRACE_TEST_NAME trace_selftest_dynamic_test_func
+#define __STR(x) #x
+#define STR(x) __STR(x)
+static int DYN_FTRACE_TEST_NAME(void)
+{
+       /* used to call mcount */
+       return 0;
+}
+
+/* Test dynamic code modification and ftrace filters */
+int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
+                                          struct trace_array *tr,
+                                          int (*func)(void))
+{
+       unsigned long count;
+       int ret;
+       int save_ftrace_enabled = ftrace_enabled;
+       int save_tracer_enabled = tracer_enabled;
+
+       /* The ftrace test PASSED */
+       printk(KERN_CONT "PASSED\n");
+       pr_info("Testing dynamic ftrace: ");
+
+       /* enable tracing, and record the filter function */
+       ftrace_enabled = 1;
+       tracer_enabled = 1;
+
+       /* passed in by parameter to fool gcc from optimizing */
+       func();
+
+       /* update the records */
+       ret = ftrace_force_update();
+       if (ret) {
+               printk(KERN_CONT ".. ftraced failed .. ");
+               return ret;
+       }
+
+       /* filter only on our function */
+       ftrace_set_filter(STR(DYN_FTRACE_TEST_NAME),
+                         sizeof(STR(DYN_FTRACE_TEST_NAME)), 1);
+
+       /* enable tracing */
+       tr->ctrl = 1;
+       trace->init(tr);
+       /* Sleep for a 1/10 of a second */
+       msleep(100);
+
+       /* we should have nothing in the buffer */
+       ret = trace_test_buffer(tr, &count);
+       if (ret)
+               goto out;
+
+       if (count) {
+               ret = -1;
+               printk(KERN_CONT ".. filter did not filter .. ");
+               goto out;
+       }
+
+       /* call our function again */
+       func();
+
+       /* sleep again */
+       msleep(100);
+
+       /* stop the tracing. */
+       tr->ctrl = 0;
+       trace->ctrl_update(tr);
+       ftrace_enabled = 0;
+
+       /* check the trace buffer */
+       ret = trace_test_buffer(tr, &count);
+       trace->reset(tr);
+
+       /* we should only have one item */
+       if (!ret && count != 1) {
+               printk(KERN_CONT ".. filter failed ..");
+               ret = -1;
+               goto out;
+       }
+ out:
+       ftrace_enabled = save_ftrace_enabled;
+       tracer_enabled = save_tracer_enabled;
+
+       /* Enable tracing on all functions again */
+       ftrace_set_filter(NULL, 0, 1);
+
+       return ret;
+}
+#else
+# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; })
+#endif /* CONFIG_DYNAMIC_FTRACE */
 /*
  * Simple verification test of ftrace function tracer.
  * Enable ftrace, sleep 1/10 second, and then read the trace
@@ -109,8 +203,13 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
 {
        unsigned long count;
        int ret;
+       int save_ftrace_enabled = ftrace_enabled;
+       int save_tracer_enabled = tracer_enabled;
 
-       /* make sure functions have been recorded */
+       /* make sure msleep has been recorded */
+       msleep(1);
+
+       /* force the recorded functions to be traced */
        ret = ftrace_force_update();
        if (ret) {
                printk(KERN_CONT ".. ftraced failed .. ");
@@ -119,6 +218,7 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
 
        /* start the tracing */
        ftrace_enabled = 1;
+       tracer_enabled = 1;
 
        tr->ctrl = 1;
        trace->init(tr);
@@ -136,8 +236,16 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
        if (!ret && !count) {
                printk(KERN_CONT ".. no entries found ..");
                ret = -1;
+               goto out;
        }
 
+       ret = trace_selftest_startup_dynamic_tracing(trace, tr,
+                                                    DYN_FTRACE_TEST_NAME);
+
+ out:
+       ftrace_enabled = save_ftrace_enabled;
+       tracer_enabled = save_tracer_enabled;
+
        return ret;
 }
 #endif /* CONFIG_FTRACE */
@@ -415,6 +523,3 @@ trace_selftest_startup_sched_switch(struct tracer *trace, struct trace_array *tr
        return ret;
 }
 #endif /* CONFIG_CONTEXT_SWITCH_TRACER */
-
-#ifdef CONFIG_DYNAMIC_FTRACE
-#endif /* CONFIG_DYNAMIC_FTRACE */