Skip to content

Commit

Permalink
ftrace: startup tester on dynamic tracing.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Steven Rostedt authored and Thomas Gleixner committed May 23, 2008
1 parent 7bd2f24 commit 77a2b37
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 4 deletions.
2 changes: 2 additions & 0 deletions include/linux/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
Expand Down
19 changes: 19 additions & 0 deletions kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
113 changes: 109 additions & 4 deletions kernel/trace/trace_selftest.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 .. ");
Expand All @@ -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);
Expand All @@ -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 */
Expand Down Expand Up @@ -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 */

0 comments on commit 77a2b37

Please sign in to comment.