mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-19 10:14:23 +08:00
ftrace: Add selftest to test function trace recursion protection
Add selftests to test the function tracing recursion protection actually does work. It also tests if a ftrace_ops states it will perform its own protection. Although, even if the ftrace_ops states it will protect itself, the ftrace infrastructure may still provide protection if the arch does not support all features or another ftrace_ops is registered. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
parent
47239c4d8d
commit
ea701f11da
@ -220,6 +220,10 @@ extern void ftrace_stub(unsigned long a0, unsigned long a1,
|
|||||||
*/
|
*/
|
||||||
#define register_ftrace_function(ops) ({ 0; })
|
#define register_ftrace_function(ops) ({ 0; })
|
||||||
#define unregister_ftrace_function(ops) ({ 0; })
|
#define unregister_ftrace_function(ops) ({ 0; })
|
||||||
|
static inline int ftrace_nr_registered_ops(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
static inline void clear_ftrace_function(void) { }
|
static inline void clear_ftrace_function(void) { }
|
||||||
static inline void ftrace_kill(void) { }
|
static inline void ftrace_kill(void) { }
|
||||||
static inline void ftrace_stop(void) { }
|
static inline void ftrace_stop(void) { }
|
||||||
@ -275,6 +279,8 @@ extern void unregister_ftrace_function_probe_all(char *glob);
|
|||||||
|
|
||||||
extern int ftrace_text_reserved(void *start, void *end);
|
extern int ftrace_text_reserved(void *start, void *end);
|
||||||
|
|
||||||
|
extern int ftrace_nr_registered_ops(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The dyn_ftrace record's flags field is split into two parts.
|
* The dyn_ftrace record's flags field is split into two parts.
|
||||||
* the first part which is '0-FTRACE_REF_MAX' is a counter of
|
* the first part which is '0-FTRACE_REF_MAX' is a counter of
|
||||||
|
@ -111,6 +111,27 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip);
|
|||||||
#define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops)
|
#define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ftrace_nr_registered_ops - return number of ops registered
|
||||||
|
*
|
||||||
|
* Returns the number of ftrace_ops registered and tracing functions
|
||||||
|
*/
|
||||||
|
int ftrace_nr_registered_ops(void)
|
||||||
|
{
|
||||||
|
struct ftrace_ops *ops;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
mutex_lock(&ftrace_lock);
|
||||||
|
|
||||||
|
for (ops = ftrace_ops_list;
|
||||||
|
ops != &ftrace_list_end; ops = ops->next)
|
||||||
|
cnt++;
|
||||||
|
|
||||||
|
mutex_unlock(&ftrace_lock);
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Traverse the ftrace_global_list, invoking all entries. The reason that we
|
* Traverse the ftrace_global_list, invoking all entries. The reason that we
|
||||||
* can use rcu_dereference_raw() is that elements removed from this list
|
* can use rcu_dereference_raw() is that elements removed from this list
|
||||||
|
@ -406,8 +406,141 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int trace_selftest_recursion_cnt;
|
||||||
|
static void trace_selftest_test_recursion_func(unsigned long ip,
|
||||||
|
unsigned long pip,
|
||||||
|
struct ftrace_ops *op,
|
||||||
|
struct pt_regs *pt_regs)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This function is registered without the recursion safe flag.
|
||||||
|
* The ftrace infrastructure should provide the recursion
|
||||||
|
* protection. If not, this will crash the kernel!
|
||||||
|
*/
|
||||||
|
trace_selftest_recursion_cnt++;
|
||||||
|
DYN_FTRACE_TEST_NAME();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trace_selftest_test_recursion_safe_func(unsigned long ip,
|
||||||
|
unsigned long pip,
|
||||||
|
struct ftrace_ops *op,
|
||||||
|
struct pt_regs *pt_regs)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We said we would provide our own recursion. By calling
|
||||||
|
* this function again, we should recurse back into this function
|
||||||
|
* and count again. But this only happens if the arch supports
|
||||||
|
* all of ftrace features and nothing else is using the function
|
||||||
|
* tracing utility.
|
||||||
|
*/
|
||||||
|
if (trace_selftest_recursion_cnt++)
|
||||||
|
return;
|
||||||
|
DYN_FTRACE_TEST_NAME();
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ftrace_ops test_rec_probe = {
|
||||||
|
.func = trace_selftest_test_recursion_func,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ftrace_ops test_recsafe_probe = {
|
||||||
|
.func = trace_selftest_test_recursion_safe_func,
|
||||||
|
.flags = FTRACE_OPS_FL_RECURSION_SAFE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
trace_selftest_function_recursion(void)
|
||||||
|
{
|
||||||
|
int save_ftrace_enabled = ftrace_enabled;
|
||||||
|
int save_tracer_enabled = tracer_enabled;
|
||||||
|
char *func_name;
|
||||||
|
int len;
|
||||||
|
int ret;
|
||||||
|
int cnt;
|
||||||
|
|
||||||
|
/* The previous test PASSED */
|
||||||
|
pr_cont("PASSED\n");
|
||||||
|
pr_info("Testing ftrace recursion: ");
|
||||||
|
|
||||||
|
|
||||||
|
/* enable tracing, and record the filter function */
|
||||||
|
ftrace_enabled = 1;
|
||||||
|
tracer_enabled = 1;
|
||||||
|
|
||||||
|
/* Handle PPC64 '.' name */
|
||||||
|
func_name = "*" __stringify(DYN_FTRACE_TEST_NAME);
|
||||||
|
len = strlen(func_name);
|
||||||
|
|
||||||
|
ret = ftrace_set_filter(&test_rec_probe, func_name, len, 1);
|
||||||
|
if (ret) {
|
||||||
|
pr_cont("*Could not set filter* ");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = register_ftrace_function(&test_rec_probe);
|
||||||
|
if (ret) {
|
||||||
|
pr_cont("*could not register callback* ");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
DYN_FTRACE_TEST_NAME();
|
||||||
|
|
||||||
|
unregister_ftrace_function(&test_rec_probe);
|
||||||
|
|
||||||
|
ret = -1;
|
||||||
|
if (trace_selftest_recursion_cnt != 1) {
|
||||||
|
pr_cont("*callback not called once (%d)* ",
|
||||||
|
trace_selftest_recursion_cnt);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_selftest_recursion_cnt = 1;
|
||||||
|
|
||||||
|
pr_cont("PASSED\n");
|
||||||
|
pr_info("Testing ftrace recursion safe: ");
|
||||||
|
|
||||||
|
ret = ftrace_set_filter(&test_recsafe_probe, func_name, len, 1);
|
||||||
|
if (ret) {
|
||||||
|
pr_cont("*Could not set filter* ");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = register_ftrace_function(&test_recsafe_probe);
|
||||||
|
if (ret) {
|
||||||
|
pr_cont("*could not register callback* ");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
DYN_FTRACE_TEST_NAME();
|
||||||
|
|
||||||
|
unregister_ftrace_function(&test_recsafe_probe);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If arch supports all ftrace features, and no other task
|
||||||
|
* was on the list, we should be fine.
|
||||||
|
*/
|
||||||
|
if (!ftrace_nr_registered_ops() && !FTRACE_FORCE_LIST_FUNC)
|
||||||
|
cnt = 2; /* Should have recursed */
|
||||||
|
else
|
||||||
|
cnt = 1;
|
||||||
|
|
||||||
|
ret = -1;
|
||||||
|
if (trace_selftest_recursion_cnt != cnt) {
|
||||||
|
pr_cont("*callback not called expected %d times (%d)* ",
|
||||||
|
cnt, trace_selftest_recursion_cnt);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
ftrace_enabled = save_ftrace_enabled;
|
||||||
|
tracer_enabled = save_tracer_enabled;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; })
|
# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; })
|
||||||
|
# define trace_selftest_function_recursion() ({ 0; })
|
||||||
#endif /* CONFIG_DYNAMIC_FTRACE */
|
#endif /* CONFIG_DYNAMIC_FTRACE */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -455,7 +588,10 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
|
|||||||
|
|
||||||
ret = trace_selftest_startup_dynamic_tracing(trace, tr,
|
ret = trace_selftest_startup_dynamic_tracing(trace, tr,
|
||||||
DYN_FTRACE_TEST_NAME);
|
DYN_FTRACE_TEST_NAME);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = trace_selftest_function_recursion();
|
||||||
out:
|
out:
|
||||||
ftrace_enabled = save_ftrace_enabled;
|
ftrace_enabled = save_ftrace_enabled;
|
||||||
tracer_enabled = save_tracer_enabled;
|
tracer_enabled = save_tracer_enabled;
|
||||||
|
Loading…
Reference in New Issue
Block a user