Commit 77a2b37d authored by Steven Rostedt's avatar Steven Rostedt Committed by Thomas Gleixner

ftrace: startup tester on dynamic tracing.

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: default avatarSteven Rostedt <srostedt@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 7bd2f24c
...@@ -55,6 +55,7 @@ struct dyn_ftrace { ...@@ -55,6 +55,7 @@ struct dyn_ftrace {
}; };
int ftrace_force_update(void); int ftrace_force_update(void);
void ftrace_set_filter(unsigned char *buf, int len, int reset);
/* defined in arch */ /* defined in arch */
extern int ftrace_ip_converted(unsigned long ip); extern int ftrace_ip_converted(unsigned long ip);
...@@ -70,6 +71,7 @@ extern void ftrace_call(void); ...@@ -70,6 +71,7 @@ extern void ftrace_call(void);
extern void mcount_call(void); extern void mcount_call(void);
#else #else
# define ftrace_force_update() ({ 0; }) # define ftrace_force_update() ({ 0; })
# define ftrace_set_filter(buf, len, reset) do { } while (0)
#endif #endif
static inline void tracer_disable(void) static inline void tracer_disable(void)
......
...@@ -1010,6 +1010,25 @@ ftrace_filter_write(struct file *file, const char __user *ubuf, ...@@ -1010,6 +1010,25 @@ ftrace_filter_write(struct file *file, const char __user *ubuf,
return ret; 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 static int notrace
ftrace_filter_release(struct inode *inode, struct file *file) ftrace_filter_release(struct inode *inode, struct file *file)
{ {
......
...@@ -99,6 +99,100 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count) ...@@ -99,6 +99,100 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count)
} }
#ifdef CONFIG_FTRACE #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. * Simple verification test of ftrace function tracer.
* Enable ftrace, sleep 1/10 second, and then read the trace * 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) ...@@ -109,8 +203,13 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
{ {
unsigned long count; unsigned long count;
int ret; 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(); ret = ftrace_force_update();
if (ret) { if (ret) {
printk(KERN_CONT ".. ftraced failed .. "); printk(KERN_CONT ".. ftraced failed .. ");
...@@ -119,6 +218,7 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) ...@@ -119,6 +218,7 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
/* start the tracing */ /* start the tracing */
ftrace_enabled = 1; ftrace_enabled = 1;
tracer_enabled = 1;
tr->ctrl = 1; tr->ctrl = 1;
trace->init(tr); trace->init(tr);
...@@ -136,8 +236,16 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) ...@@ -136,8 +236,16 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
if (!ret && !count) { if (!ret && !count) {
printk(KERN_CONT ".. no entries found .."); printk(KERN_CONT ".. no entries found ..");
ret = -1; 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; return ret;
} }
#endif /* CONFIG_FTRACE */ #endif /* CONFIG_FTRACE */
...@@ -415,6 +523,3 @@ trace_selftest_startup_sched_switch(struct tracer *trace, struct trace_array *tr ...@@ -415,6 +523,3 @@ trace_selftest_startup_sched_switch(struct tracer *trace, struct trace_array *tr
return ret; return ret;
} }
#endif /* CONFIG_CONTEXT_SWITCH_TRACER */ #endif /* CONFIG_CONTEXT_SWITCH_TRACER */
#ifdef CONFIG_DYNAMIC_FTRACE
#endif /* CONFIG_DYNAMIC_FTRACE */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment