Message ID | 170723228217.502590.6615001674278328094.stgit@devnote2 (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph | expand |
On Wed, 7 Feb 2024 00:11:22 +0900 "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > From: Steven Rostedt (VMware) <rostedt@goodmis.org> > > Add boot up selftest that passes variables from a function entry to a > function exit, and make sure that they do get passed around. > > Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org> > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> > --- > Changes in v2: > - Add reserved size test. > - Use pr_*() instead of printk(KERN_*). > --- > kernel/trace/trace_selftest.c | 169 +++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 169 insertions(+) > > diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c > index f0758afa2f7d..4d86cd4c8c8c 100644 > --- a/kernel/trace/trace_selftest.c > +++ b/kernel/trace/trace_selftest.c > @@ -756,6 +756,173 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) > > #ifdef CONFIG_FUNCTION_GRAPH_TRACER > > +#ifdef CONFIG_DYNAMIC_FTRACE > + > +#define BYTE_NUMBER 123 > +#define SHORT_NUMBER 12345 > +#define WORD_NUMBER 1234567890 > +#define LONG_NUMBER 1234567890123456789LL > + > +static int fgraph_store_size __initdata; > +static const char *fgraph_store_type_name __initdata; > +static char *fgraph_error_str __initdata; > +static char fgraph_error_str_buf[128] __initdata; > + > +static __init int store_entry(struct ftrace_graph_ent *trace, > + struct fgraph_ops *gops) > +{ > + const char *type = fgraph_store_type_name; > + int size = fgraph_store_size; > + void *p; > + > + p = fgraph_reserve_data(gops->idx, size); > + if (!p) { > + snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf), > + "Failed to reserve %s\n", type); > + fgraph_error_str = fgraph_error_str_buf; > + return 0; > + } > + > + switch (fgraph_store_size) { > + case 1: > + *(char *)p = BYTE_NUMBER; > + break; > + case 2: > + *(short *)p = SHORT_NUMBER; > + break; > + case 4: > + *(int *)p = WORD_NUMBER; > + break; > + case 8: > + *(long long *)p = LONG_NUMBER; > + break; > + } > + What would be an interesting test is to run all versions together. That is, to attach a callback that stores a byte, a callback that stores a short, a callback that stores a word and a callback that stores a long, and attach them all to the same function. I guess we can add that as a separate patch. -- Steve > + return 1; > +} > +
On Thu, 15 Feb 2024 10:02:54 -0500 Steven Rostedt <rostedt@goodmis.org> wrote: > On Wed, 7 Feb 2024 00:11:22 +0900 > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > > > From: Steven Rostedt (VMware) <rostedt@goodmis.org> > > > > Add boot up selftest that passes variables from a function entry to a > > function exit, and make sure that they do get passed around. > > > > Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org> > > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> > > --- > > Changes in v2: > > - Add reserved size test. > > - Use pr_*() instead of printk(KERN_*). > > --- > > kernel/trace/trace_selftest.c | 169 +++++++++++++++++++++++++++++++++++++++++ > > 1 file changed, 169 insertions(+) > > > > diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c > > index f0758afa2f7d..4d86cd4c8c8c 100644 > > --- a/kernel/trace/trace_selftest.c > > +++ b/kernel/trace/trace_selftest.c > > @@ -756,6 +756,173 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) > > > > #ifdef CONFIG_FUNCTION_GRAPH_TRACER > > > > +#ifdef CONFIG_DYNAMIC_FTRACE > > + > > +#define BYTE_NUMBER 123 > > +#define SHORT_NUMBER 12345 > > +#define WORD_NUMBER 1234567890 > > +#define LONG_NUMBER 1234567890123456789LL > > + > > +static int fgraph_store_size __initdata; > > +static const char *fgraph_store_type_name __initdata; > > +static char *fgraph_error_str __initdata; > > +static char fgraph_error_str_buf[128] __initdata; > > + > > +static __init int store_entry(struct ftrace_graph_ent *trace, > > + struct fgraph_ops *gops) > > +{ > > + const char *type = fgraph_store_type_name; > > + int size = fgraph_store_size; > > + void *p; > > + > > + p = fgraph_reserve_data(gops->idx, size); > > + if (!p) { > > + snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf), > > + "Failed to reserve %s\n", type); > > + fgraph_error_str = fgraph_error_str_buf; > > + return 0; > > + } > > + > > + switch (fgraph_store_size) { > > + case 1: > > + *(char *)p = BYTE_NUMBER; > > + break; > > + case 2: > > + *(short *)p = SHORT_NUMBER; > > + break; > > + case 4: > > + *(int *)p = WORD_NUMBER; > > + break; > > + case 8: > > + *(long long *)p = LONG_NUMBER; > > + break; > > + } > > + > > What would be an interesting test is to run all versions together. That is, > to attach a callback that stores a byte, a callback that stores a short, a > callback that stores a word and a callback that stores a long, and attach > them all to the same function. > > I guess we can add that as a separate patch. Would you mean we should have different callbacks which stores the different size of data instead of using switch()? Thank you, > > -- Steve > > > > + return 1; > > +} > > +
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index f0758afa2f7d..4d86cd4c8c8c 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -756,6 +756,173 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) #ifdef CONFIG_FUNCTION_GRAPH_TRACER +#ifdef CONFIG_DYNAMIC_FTRACE + +#define BYTE_NUMBER 123 +#define SHORT_NUMBER 12345 +#define WORD_NUMBER 1234567890 +#define LONG_NUMBER 1234567890123456789LL + +static int fgraph_store_size __initdata; +static const char *fgraph_store_type_name __initdata; +static char *fgraph_error_str __initdata; +static char fgraph_error_str_buf[128] __initdata; + +static __init int store_entry(struct ftrace_graph_ent *trace, + struct fgraph_ops *gops) +{ + const char *type = fgraph_store_type_name; + int size = fgraph_store_size; + void *p; + + p = fgraph_reserve_data(gops->idx, size); + if (!p) { + snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf), + "Failed to reserve %s\n", type); + fgraph_error_str = fgraph_error_str_buf; + return 0; + } + + switch (fgraph_store_size) { + case 1: + *(char *)p = BYTE_NUMBER; + break; + case 2: + *(short *)p = SHORT_NUMBER; + break; + case 4: + *(int *)p = WORD_NUMBER; + break; + case 8: + *(long long *)p = LONG_NUMBER; + break; + } + + return 1; +} + +static __init void store_return(struct ftrace_graph_ret *trace, + struct fgraph_ops *gops) +{ + const char *type = fgraph_store_type_name; + long long expect = 0; + long long found = -1; + int size; + char *p; + + p = fgraph_retrieve_data(gops->idx, &size); + if (!p) { + snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf), + "Failed to retrieve %s\n", type); + fgraph_error_str = fgraph_error_str_buf; + return; + } + if (fgraph_store_size > size) { + snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf), + "Retrieved size %d is smaller than expected %d\n", + size, (int)fgraph_store_size); + fgraph_error_str = fgraph_error_str_buf; + return; + } + + switch (fgraph_store_size) { + case 1: + expect = BYTE_NUMBER; + found = *(char *)p; + break; + case 2: + expect = SHORT_NUMBER; + found = *(short *)p; + break; + case 4: + expect = WORD_NUMBER; + found = *(int *)p; + break; + case 8: + expect = LONG_NUMBER; + found = *(long long *)p; + break; + } + + if (found != expect) { + snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf), + "%s returned not %lld but %lld\n", type, expect, found); + fgraph_error_str = fgraph_error_str_buf; + return; + } + fgraph_error_str = NULL; +} + +static struct fgraph_ops store_bytes __initdata = { + .entryfunc = store_entry, + .retfunc = store_return, +}; + +static int __init test_graph_storage_type(const char *name, int size) +{ + char *func_name; + int len; + int ret; + + fgraph_store_type_name = name; + fgraph_store_size = size; + + snprintf(fgraph_error_str_buf, sizeof(fgraph_error_str_buf), + "Failed to execute storage %s\n", name); + fgraph_error_str = fgraph_error_str_buf; + + pr_cont("PASSED\n"); + pr_info("Testing fgraph storage of %d byte%s: ", size, size > 1 ? "s" : ""); + + func_name = "*" __stringify(DYN_FTRACE_TEST_NAME); + len = strlen(func_name); + + ret = ftrace_set_filter(&store_bytes.ops, func_name, len, 1); + if (ret && ret != -ENODEV) { + pr_cont("*Could not set filter* "); + return -1; + } + + ret = register_ftrace_graph(&store_bytes); + if (ret) { + pr_warn("Failed to init store_bytes fgraph tracing\n"); + return -1; + } + + DYN_FTRACE_TEST_NAME(); + + unregister_ftrace_graph(&store_bytes); + + if (fgraph_error_str) { + pr_cont("*** %s ***", fgraph_error_str); + return -1; + } + + return 0; +} +/* Test the storage passed across function_graph entry and return */ +static __init int test_graph_storage(void) +{ + int ret; + + ret = test_graph_storage_type("byte", 1); + if (ret) + return ret; + ret = test_graph_storage_type("short", 2); + if (ret) + return ret; + ret = test_graph_storage_type("word", 4); + if (ret) + return ret; + ret = test_graph_storage_type("long long", 8); + if (ret) + return ret; + return 0; +} +#else +static inline int test_graph_storage(void) { return 0; } +#endif /* CONFIG_DYNAMIC_FTRACE */ + /* Maximum number of functions to trace before diagnosing a hang */ #define GRAPH_MAX_FUNC_TEST 100000000 @@ -913,6 +1080,8 @@ trace_selftest_startup_function_graph(struct tracer *trace, ftrace_set_global_filter(NULL, 0, 1); #endif + ret = test_graph_storage(); + /* Don't test dynamic tracing, the function tracer already did */ out: /* Stop it if we failed */