Message ID | 170723230476.502590.16817817024423790038.stgit@devnote2 (mailing list archive) |
---|---|
State | Not Applicable |
Headers | show |
Series | tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph | expand |
On Wed, 7 Feb 2024 00:11:44 +0900 "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > From: Masami Hiramatsu (Google) <mhiramat@kernel.org> > > Add a new return handler to fgraph_ops as 'retregfunc' which takes > parent_ip and ftrace_regs instead of ftrace_graph_ret. This handler > is available only if the arch support CONFIG_HAVE_FUNCTION_GRAPH_FREGS. > Note that the 'retfunc' and 'reregfunc' are mutual exclusive. > You can set only one of them. > > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> > > @@ -1076,6 +1083,7 @@ struct fgraph_ops { > trace_func_graph_ent_t entryfunc; > trace_func_graph_ret_t retfunc; > trace_func_graph_regs_ent_t entryregfunc; > + trace_func_graph_regs_ret_t retregfunc; Same for this: struct fgraph_ops { union { trace_func_graph_ent_t entryfunc; trace_func_graph_regs_ent_t entryregfunc; }; union { trace_func_graph_ret_t retfunc; trace_func_graph_regs_ret_t retregfunc; } -- Steve > struct ftrace_ops ops; /* for the hash lists */ > void *private; > int idx;
On Wed, 7 Feb 2024 00:11:44 +0900 "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig > index 61c541c36596..308b3bec01b1 100644 > --- a/kernel/trace/Kconfig > +++ b/kernel/trace/Kconfig > @@ -34,6 +34,9 @@ config HAVE_FUNCTION_GRAPH_TRACER > config HAVE_FUNCTION_GRAPH_RETVAL > bool > > +config HAVE_FUNCTION_GRAPH_FREGS > + bool > + > config HAVE_DYNAMIC_FTRACE > bool > help We're starting to get overloaded with the CONFIG_HAVE_* options. We need to start consolidating them. I would like to make RETVAL and FREGS into one option. We can add this now, but before we add anything else, we need to see what HAVE configs have the same archs, and then just consolidate them. If an new arch wants to add one of the consolidated features, it will also need to add all the other features that were consolidated with it. -- Steve
On Wed, 7 Feb 2024 00:11:44 +0900 "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h > index c88bf47f46da..a061f8832b20 100644 > --- a/arch/x86/include/asm/ftrace.h > +++ b/arch/x86/include/asm/ftrace.h > @@ -72,6 +72,8 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs) > override_function_with_return(&(fregs)->regs) > #define ftrace_regs_query_register_offset(name) \ > regs_query_register_offset(name) > +#define ftrace_regs_get_frame_pointer(fregs) \ > + frame_pointer(&(fregs)->regs) > Doesn't the above belong in the next patch that implements this for x86? > struct ftrace_ops; > #define ftrace_graph_func ftrace_graph_func > diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h > index 65d4d4b68768..da2a23f5a9ed 100644 > --- a/include/linux/ftrace.h > +++ b/include/linux/ftrace.h > @@ -43,7 +43,9 @@ struct dyn_ftrace; > > char *arch_ftrace_match_adjust(char *str, const char *search); > > -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL > +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS > +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs); > +#elif defined(CONFIG_HAVE_FUNCTION_GRAPH_RETVAL) > struct fgraph_ret_regs; > unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs); > #else > @@ -157,6 +159,7 @@ struct ftrace_regs { > #define ftrace_regs_set_instruction_pointer(fregs, ip) do { } while (0) > #endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ > > + spurious newline. > static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs) > { > if (!fregs) > @@ -1067,6 +1070,10 @@ typedef int (*trace_func_graph_regs_ent_t)(unsigned long func, > unsigned long parent_ip, > struct ftrace_regs *fregs, > struct fgraph_ops *); /* entry w/ regs */ > +typedef void (*trace_func_graph_regs_ret_t)(unsigned long func, > + unsigned long parent_ip, > + struct ftrace_regs *, > + struct fgraph_ops *); /* return w/ regs */ > > extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops); > > @@ -1076,6 +1083,7 @@ struct fgraph_ops { > trace_func_graph_ent_t entryfunc; > trace_func_graph_ret_t retfunc; > trace_func_graph_regs_ent_t entryregfunc; > + trace_func_graph_regs_ret_t retregfunc; > struct ftrace_ops ops; /* for the hash lists */ > void *private; > int idx; > diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig > index 61c541c36596..308b3bec01b1 100644 > --- a/kernel/trace/Kconfig > +++ b/kernel/trace/Kconfig > @@ -34,6 +34,9 @@ config HAVE_FUNCTION_GRAPH_TRACER > config HAVE_FUNCTION_GRAPH_RETVAL > bool > > +config HAVE_FUNCTION_GRAPH_FREGS > + bool > + > config HAVE_DYNAMIC_FTRACE > bool > help > @@ -232,7 +235,7 @@ config FUNCTION_GRAPH_TRACER > > config FUNCTION_GRAPH_RETVAL > bool "Kernel Function Graph Return Value" > - depends on HAVE_FUNCTION_GRAPH_RETVAL > + depends on HAVE_FUNCTION_GRAPH_RETVAL || HAVE_FUNCTION_GRAPH_FREGS > depends on FUNCTION_GRAPH_TRACER > default n > help > diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c > index 459912ca72e0..12e5f108e242 100644 > --- a/kernel/trace/fgraph.c > +++ b/kernel/trace/fgraph.c > @@ -752,8 +752,8 @@ int function_graph_enter_ops(unsigned long ret, unsigned long func, > > /* Retrieve a function return address to the trace stack on thread info.*/ > static struct ftrace_ret_stack * > -ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, > - unsigned long frame_pointer, int *index) > +ftrace_pop_return_trace(unsigned long *ret, unsigned long frame_pointer, > + int *index) > { > struct ftrace_ret_stack *ret_stack; > > @@ -798,10 +798,6 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, > > *index += FGRAPH_RET_INDEX; > *ret = ret_stack->ret; > - trace->func = ret_stack->func; > - trace->calltime = ret_stack->calltime; > - trace->overrun = atomic_read(¤t->trace_overrun); > - trace->depth = current->curr_ret_depth; There's a lot of information stored in the trace structure. Why not pass that to the new retregfunc? Then you don't need to separate this code out. > /* > * We still want to trace interrupts coming in if > * max_depth is set to 1. Make sure the decrement is > @@ -840,21 +836,42 @@ static struct notifier_block ftrace_suspend_notifier = { > /* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */ > struct fgraph_ret_regs; > > +static void fgraph_call_retfunc(struct ftrace_regs *fregs, > + struct fgraph_ret_regs *ret_regs, > + struct ftrace_ret_stack *ret_stack, > + struct fgraph_ops *gops) > +{ > + struct ftrace_graph_ret trace; > + > + trace.func = ret_stack->func; > + trace.calltime = ret_stack->calltime; > + trace.overrun = atomic_read(¤t->trace_overrun); > + trace.depth = current->curr_ret_depth; > + trace.rettime = trace_clock_local(); > +#ifdef CONFIG_FUNCTION_GRAPH_RETVAL > + if (fregs) > + trace.retval = ftrace_regs_get_return_value(fregs); > + else > + trace.retval = fgraph_ret_regs_return_value(ret_regs); > +#endif > + gops->retfunc(&trace, gops); > +} > + > /* > * Send the trace to the ring-buffer. > * @return the original return address. > */ > -static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs, > +static unsigned long __ftrace_return_to_handler(struct ftrace_regs *fregs, > + struct fgraph_ret_regs *ret_regs, > unsigned long frame_pointer) > { > struct ftrace_ret_stack *ret_stack; > - struct ftrace_graph_ret trace; > unsigned long bitmap; > unsigned long ret; > int index; > int i; > > - ret_stack = ftrace_pop_return_trace(&trace, &ret, frame_pointer, &index); > + ret_stack = ftrace_pop_return_trace(&ret, frame_pointer, &index); > > if (unlikely(!ret_stack)) { > ftrace_graph_stop(); > @@ -863,10 +880,8 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs > return (unsigned long)panic; > } > > - trace.rettime = trace_clock_local(); > -#ifdef CONFIG_FUNCTION_GRAPH_RETVAL > - trace.retval = fgraph_ret_regs_return_value(ret_regs); > -#endif > + if (fregs) > + ftrace_regs_set_instruction_pointer(fregs, ret); > > bitmap = get_fgraph_index_bitmap(current, index); > for (i = 0; i < FGRAPH_ARRAY_SIZE; i++) { > @@ -877,7 +892,10 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs > if (gops == &fgraph_stub) > continue; > > - gops->retfunc(&trace, gops); > + if (gops->retregfunc) > + gops->retregfunc(ret_stack->func, ret, fregs, gops); > + else > + fgraph_call_retfunc(fregs, ret_regs, ret_stack, gops); > } > > /* > @@ -892,20 +910,22 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs > return ret; > } > > -/* > - * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can > - * leave only ftrace_return_to_handler(ret_regs). > - */ > -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL > +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS > +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs) > +{ > + return __ftrace_return_to_handler(fregs, NULL, > + ftrace_regs_get_frame_pointer(fregs)); > +} > +#elif defined(CONFIG_HAVE_FUNCTION_GRAPH_RETVAL) > unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs) > { > - return __ftrace_return_to_handler(ret_regs, > + return __ftrace_return_to_handler(NULL, ret_regs, > fgraph_ret_regs_frame_pointer(ret_regs)); > } > #else > unsigned long ftrace_return_to_handler(unsigned long frame_pointer) > { > - return __ftrace_return_to_handler(NULL, frame_pointer); > + return __ftrace_return_to_handler(NULL, NULL, frame_pointer); > } > #endif > > @@ -1262,9 +1282,15 @@ int register_ftrace_graph(struct fgraph_ops *gops) > int ret = 0; > int i; > > - if (gops->entryfunc && gops->entryregfunc) > + if ((gops->entryfunc && gops->entryregfunc) || > + (gops->retfunc && gops->retregfunc)) > return -EINVAL; With a union, you don't need this. Now, is it possible to have a entryregfunc and a retfunc, or a entryfunc and a retregfunc? -- Steve > > +#ifndef CONFIG_HAVE_FUNCTION_GRAPH_FREGS > + if (gops->retregfunc) > + return -EOPNOTSUPP; > +#endif > + > mutex_lock(&ftrace_lock); > > if (!gops->ops.func) {
On Thu, 15 Feb 2024 10:39:43 -0500 Steven Rostedt <rostedt@goodmis.org> wrote: > On Wed, 7 Feb 2024 00:11:44 +0900 > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > > > From: Masami Hiramatsu (Google) <mhiramat@kernel.org> > > > > Add a new return handler to fgraph_ops as 'retregfunc' which takes > > parent_ip and ftrace_regs instead of ftrace_graph_ret. This handler > > is available only if the arch support CONFIG_HAVE_FUNCTION_GRAPH_FREGS. > > Note that the 'retfunc' and 'reregfunc' are mutual exclusive. > > You can set only one of them. > > > > Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> > > > > > @@ -1076,6 +1083,7 @@ struct fgraph_ops { > > trace_func_graph_ent_t entryfunc; > > trace_func_graph_ret_t retfunc; > > trace_func_graph_regs_ent_t entryregfunc; > > + trace_func_graph_regs_ret_t retregfunc; > > Same for this: > > struct fgraph_ops { > union { > trace_func_graph_ent_t entryfunc; > trace_func_graph_regs_ent_t entryregfunc; > }; > union { > trace_func_graph_ret_t retfunc; > trace_func_graph_regs_ret_t retregfunc; > } OK, and we need to introduce a flag for fgraph_ops that it is using `regfunc` or not. Thank you, > > -- Steve > > > > struct ftrace_ops ops; /* for the hash lists */ > > void *private; > > int idx;
On Thu, 15 Feb 2024 10:49:03 -0500 Steven Rostedt <rostedt@goodmis.org> wrote: > On Wed, 7 Feb 2024 00:11:44 +0900 > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > > > diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig > > index 61c541c36596..308b3bec01b1 100644 > > --- a/kernel/trace/Kconfig > > +++ b/kernel/trace/Kconfig > > @@ -34,6 +34,9 @@ config HAVE_FUNCTION_GRAPH_TRACER > > config HAVE_FUNCTION_GRAPH_RETVAL > > bool > > > > +config HAVE_FUNCTION_GRAPH_FREGS > > + bool > > + > > config HAVE_DYNAMIC_FTRACE > > bool > > help > > We're starting to get overloaded with the CONFIG_HAVE_* options. > > We need to start consolidating them. I would like to make RETVAL and FREGS > into one option. We can add this now, but before we add anything else, we > need to see what HAVE configs have the same archs, and then just > consolidate them. If an new arch wants to add one of the consolidated > features, it will also need to add all the other features that were > consolidated with it. Got it. So RETVAL should be implemented by FREGS or REGS. Thank you, > > -- Steve
On Thu, 15 Feb 2024 11:04:04 -0500 Steven Rostedt <rostedt@goodmis.org> wrote: > On Wed, 7 Feb 2024 00:11:44 +0900 > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > > > diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h > > index c88bf47f46da..a061f8832b20 100644 > > --- a/arch/x86/include/asm/ftrace.h > > +++ b/arch/x86/include/asm/ftrace.h > > @@ -72,6 +72,8 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs) > > override_function_with_return(&(fregs)->regs) > > #define ftrace_regs_query_register_offset(name) \ > > regs_query_register_offset(name) > > +#define ftrace_regs_get_frame_pointer(fregs) \ > > + frame_pointer(&(fregs)->regs) > > > > Doesn't the above belong in the next patch that implements this for x86? Yes, thanks for pointing it! > > > struct ftrace_ops; > > #define ftrace_graph_func ftrace_graph_func > > diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h > > index 65d4d4b68768..da2a23f5a9ed 100644 > > --- a/include/linux/ftrace.h > > +++ b/include/linux/ftrace.h > > @@ -43,7 +43,9 @@ struct dyn_ftrace; > > > > char *arch_ftrace_match_adjust(char *str, const char *search); > > > > -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL > > +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS > > +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs); > > +#elif defined(CONFIG_HAVE_FUNCTION_GRAPH_RETVAL) > > struct fgraph_ret_regs; > > unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs); > > #else > > @@ -157,6 +159,7 @@ struct ftrace_regs { > > #define ftrace_regs_set_instruction_pointer(fregs, ip) do { } while (0) > > #endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ > > > > + > > spurious newline. OK, I'll remove. > > > static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs) > > { > > if (!fregs) > > @@ -1067,6 +1070,10 @@ typedef int (*trace_func_graph_regs_ent_t)(unsigned long func, > > unsigned long parent_ip, > > struct ftrace_regs *fregs, > > struct fgraph_ops *); /* entry w/ regs */ > > +typedef void (*trace_func_graph_regs_ret_t)(unsigned long func, > > + unsigned long parent_ip, > > + struct ftrace_regs *, > > + struct fgraph_ops *); /* return w/ regs */ > > > > extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops); > > > > @@ -1076,6 +1083,7 @@ struct fgraph_ops { > > trace_func_graph_ent_t entryfunc; > > trace_func_graph_ret_t retfunc; > > trace_func_graph_regs_ent_t entryregfunc; > > + trace_func_graph_regs_ret_t retregfunc; > > struct ftrace_ops ops; /* for the hash lists */ > > void *private; > > int idx; > > diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig > > index 61c541c36596..308b3bec01b1 100644 > > --- a/kernel/trace/Kconfig > > +++ b/kernel/trace/Kconfig > > @@ -34,6 +34,9 @@ config HAVE_FUNCTION_GRAPH_TRACER > > config HAVE_FUNCTION_GRAPH_RETVAL > > bool > > > > +config HAVE_FUNCTION_GRAPH_FREGS > > + bool > > + > > config HAVE_DYNAMIC_FTRACE > > bool > > help > > @@ -232,7 +235,7 @@ config FUNCTION_GRAPH_TRACER > > > > config FUNCTION_GRAPH_RETVAL > > bool "Kernel Function Graph Return Value" > > - depends on HAVE_FUNCTION_GRAPH_RETVAL > > + depends on HAVE_FUNCTION_GRAPH_RETVAL || HAVE_FUNCTION_GRAPH_FREGS > > depends on FUNCTION_GRAPH_TRACER > > default n > > help > > diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c > > index 459912ca72e0..12e5f108e242 100644 > > --- a/kernel/trace/fgraph.c > > +++ b/kernel/trace/fgraph.c > > @@ -752,8 +752,8 @@ int function_graph_enter_ops(unsigned long ret, unsigned long func, > > > > /* Retrieve a function return address to the trace stack on thread info.*/ > > static struct ftrace_ret_stack * > > -ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, > > - unsigned long frame_pointer, int *index) > > +ftrace_pop_return_trace(unsigned long *ret, unsigned long frame_pointer, > > + int *index) > > { > > struct ftrace_ret_stack *ret_stack; > > > > @@ -798,10 +798,6 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, > > > > *index += FGRAPH_RET_INDEX; > > *ret = ret_stack->ret; > > - trace->func = ret_stack->func; > > - trace->calltime = ret_stack->calltime; > > - trace->overrun = atomic_read(¤t->trace_overrun); > > - trace->depth = current->curr_ret_depth; > > There's a lot of information stored in the trace structure. Why not pass > that to the new retregfunc? > > Then you don't need to separate this code out. Sorry, I couldn't catch what you meant, Would you mean to call ftrace_pop_return_trace() before calling retregfunc()?? because some of the information are found from ret_stack, which is poped from shadow stack. > > > /* > > * We still want to trace interrupts coming in if > > * max_depth is set to 1. Make sure the decrement is > > @@ -840,21 +836,42 @@ static struct notifier_block ftrace_suspend_notifier = { > > /* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */ > > struct fgraph_ret_regs; > > > > +static void fgraph_call_retfunc(struct ftrace_regs *fregs, > > + struct fgraph_ret_regs *ret_regs, > > + struct ftrace_ret_stack *ret_stack, > > + struct fgraph_ops *gops) > > +{ > > + struct ftrace_graph_ret trace; > > + > > + trace.func = ret_stack->func; > > + trace.calltime = ret_stack->calltime; > > + trace.overrun = atomic_read(¤t->trace_overrun); > > + trace.depth = current->curr_ret_depth; > > + trace.rettime = trace_clock_local(); > > +#ifdef CONFIG_FUNCTION_GRAPH_RETVAL > > + if (fregs) > > + trace.retval = ftrace_regs_get_return_value(fregs); > > + else > > + trace.retval = fgraph_ret_regs_return_value(ret_regs); > > +#endif > > + gops->retfunc(&trace, gops); > > +} > > + > > /* > > * Send the trace to the ring-buffer. > > * @return the original return address. > > */ > > -static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs, > > +static unsigned long __ftrace_return_to_handler(struct ftrace_regs *fregs, > > + struct fgraph_ret_regs *ret_regs, > > unsigned long frame_pointer) > > { > > struct ftrace_ret_stack *ret_stack; > > - struct ftrace_graph_ret trace; > > unsigned long bitmap; > > unsigned long ret; > > int index; > > int i; > > > > - ret_stack = ftrace_pop_return_trace(&trace, &ret, frame_pointer, &index); > > + ret_stack = ftrace_pop_return_trace(&ret, frame_pointer, &index); > > > > if (unlikely(!ret_stack)) { > > ftrace_graph_stop(); > > @@ -863,10 +880,8 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs > > return (unsigned long)panic; > > } > > > > - trace.rettime = trace_clock_local(); > > -#ifdef CONFIG_FUNCTION_GRAPH_RETVAL > > - trace.retval = fgraph_ret_regs_return_value(ret_regs); > > -#endif > > + if (fregs) > > + ftrace_regs_set_instruction_pointer(fregs, ret); > > > > bitmap = get_fgraph_index_bitmap(current, index); > > for (i = 0; i < FGRAPH_ARRAY_SIZE; i++) { > > @@ -877,7 +892,10 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs > > if (gops == &fgraph_stub) > > continue; > > > > - gops->retfunc(&trace, gops); > > + if (gops->retregfunc) > > + gops->retregfunc(ret_stack->func, ret, fregs, gops); > > + else > > + fgraph_call_retfunc(fregs, ret_regs, ret_stack, gops); > > } > > > > /* > > @@ -892,20 +910,22 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs > > return ret; > > } > > > > -/* > > - * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can > > - * leave only ftrace_return_to_handler(ret_regs). > > - */ > > -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL > > +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS > > +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs) > > +{ > > + return __ftrace_return_to_handler(fregs, NULL, > > + ftrace_regs_get_frame_pointer(fregs)); > > +} > > +#elif defined(CONFIG_HAVE_FUNCTION_GRAPH_RETVAL) > > unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs) > > { > > - return __ftrace_return_to_handler(ret_regs, > > + return __ftrace_return_to_handler(NULL, ret_regs, > > fgraph_ret_regs_frame_pointer(ret_regs)); > > } > > #else > > unsigned long ftrace_return_to_handler(unsigned long frame_pointer) > > { > > - return __ftrace_return_to_handler(NULL, frame_pointer); > > + return __ftrace_return_to_handler(NULL, NULL, frame_pointer); > > } > > #endif > > > > @@ -1262,9 +1282,15 @@ int register_ftrace_graph(struct fgraph_ops *gops) > > int ret = 0; > > int i; > > > > - if (gops->entryfunc && gops->entryregfunc) > > + if ((gops->entryfunc && gops->entryregfunc) || > > + (gops->retfunc && gops->retregfunc)) > > return -EINVAL; > > With a union, you don't need this. Indeed. > > Now, is it possible to have a entryregfunc and a retfunc, or a entryfunc > and a retregfunc? It is possisble, but may not give any benefit. Thank you, > > -- Steve > > > > > +#ifndef CONFIG_HAVE_FUNCTION_GRAPH_FREGS > > + if (gops->retregfunc) > > + return -EOPNOTSUPP; > > +#endif > > + > > mutex_lock(&ftrace_lock); > > > > if (!gops->ops.func) { >
On Fri, 16 Feb 2024 17:51:08 +0900 Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote: > > > @@ -798,10 +798,6 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, > > > > > > *index += FGRAPH_RET_INDEX; > > > *ret = ret_stack->ret; > > > - trace->func = ret_stack->func; > > > - trace->calltime = ret_stack->calltime; > > > - trace->overrun = atomic_read(¤t->trace_overrun); > > > - trace->depth = current->curr_ret_depth; > > > > There's a lot of information stored in the trace structure. Why not pass > > that to the new retregfunc? > > > > Then you don't need to separate this code out. > > Sorry, I couldn't catch what you meant, Would you mean to call > ftrace_pop_return_trace() before calling retregfunc()?? because some of the > information are found from ret_stack, which is poped from shadow stack. Ah, sorry I got what you said. I think this `trace` is not usable for the new interface. Most of the information is only used for the function-graph tracer. For example, trace->calltime and trace->overrun, trace->depth are used only for the function-graph tracer, but not for the other tracers. But yeah, this idea is considerable. It also allows us to just update entryfunc() and retfunc() to pass fgraph_regs and return address. Thank you!
Hi Steve, On Sun, 18 Feb 2024 11:53:28 +0900 Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote: > On Fri, 16 Feb 2024 17:51:08 +0900 > Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote: > > > > > @@ -798,10 +798,6 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, > > > > > > > > *index += FGRAPH_RET_INDEX; > > > > *ret = ret_stack->ret; > > > > - trace->func = ret_stack->func; > > > > - trace->calltime = ret_stack->calltime; > > > > - trace->overrun = atomic_read(¤t->trace_overrun); > > > > - trace->depth = current->curr_ret_depth; > > > > > > There's a lot of information stored in the trace structure. Why not pass > > > that to the new retregfunc? > > > > > > Then you don't need to separate this code out. > > > > Sorry, I couldn't catch what you meant, Would you mean to call > > ftrace_pop_return_trace() before calling retregfunc()?? because some of the > > information are found from ret_stack, which is poped from shadow stack. > > Ah, sorry I got what you said. I think this `trace` is not usable for the new > interface. Most of the information is only used for the function-graph tracer. > For example, trace->calltime and trace->overrun, trace->depth are used only > for the function-graph tracer, but not for the other tracers. > > But yeah, this idea is considerable. It also allows us to just update > entryfunc() and retfunc() to pass fgraph_regs and return address. The reason why I didn't use the those for *regfunc() is not only those have unused information, but those does not have some params. - ftrace_graph_ent only have current `func`, but entryregfunc() needs `parent_ip` (== return address) - ftrace_graph_ret only have current `func`, but retregfunc() needs `ret` (== return address) too. If I update the ftrace_graph_ent/ret to add 'retaddr', we can just pass ftrace_graph_ent/ret, ftrace_regs, and fgraph_ops to *regfunc(). Moreover, maybe we don't need *regfunc, but just update entryfunc/retfunc to pass ftrace_regs *, which will be NULL if it is not supported. Thank you,
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h index c88bf47f46da..a061f8832b20 100644 --- a/arch/x86/include/asm/ftrace.h +++ b/arch/x86/include/asm/ftrace.h @@ -72,6 +72,8 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs) override_function_with_return(&(fregs)->regs) #define ftrace_regs_query_register_offset(name) \ regs_query_register_offset(name) +#define ftrace_regs_get_frame_pointer(fregs) \ + frame_pointer(&(fregs)->regs) struct ftrace_ops; #define ftrace_graph_func ftrace_graph_func diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 65d4d4b68768..da2a23f5a9ed 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -43,7 +43,9 @@ struct dyn_ftrace; char *arch_ftrace_match_adjust(char *str, const char *search); -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs); +#elif defined(CONFIG_HAVE_FUNCTION_GRAPH_RETVAL) struct fgraph_ret_regs; unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs); #else @@ -157,6 +159,7 @@ struct ftrace_regs { #define ftrace_regs_set_instruction_pointer(fregs, ip) do { } while (0) #endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ + static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs) { if (!fregs) @@ -1067,6 +1070,10 @@ typedef int (*trace_func_graph_regs_ent_t)(unsigned long func, unsigned long parent_ip, struct ftrace_regs *fregs, struct fgraph_ops *); /* entry w/ regs */ +typedef void (*trace_func_graph_regs_ret_t)(unsigned long func, + unsigned long parent_ip, + struct ftrace_regs *, + struct fgraph_ops *); /* return w/ regs */ extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops); @@ -1076,6 +1083,7 @@ struct fgraph_ops { trace_func_graph_ent_t entryfunc; trace_func_graph_ret_t retfunc; trace_func_graph_regs_ent_t entryregfunc; + trace_func_graph_regs_ret_t retregfunc; struct ftrace_ops ops; /* for the hash lists */ void *private; int idx; diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 61c541c36596..308b3bec01b1 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -34,6 +34,9 @@ config HAVE_FUNCTION_GRAPH_TRACER config HAVE_FUNCTION_GRAPH_RETVAL bool +config HAVE_FUNCTION_GRAPH_FREGS + bool + config HAVE_DYNAMIC_FTRACE bool help @@ -232,7 +235,7 @@ config FUNCTION_GRAPH_TRACER config FUNCTION_GRAPH_RETVAL bool "Kernel Function Graph Return Value" - depends on HAVE_FUNCTION_GRAPH_RETVAL + depends on HAVE_FUNCTION_GRAPH_RETVAL || HAVE_FUNCTION_GRAPH_FREGS depends on FUNCTION_GRAPH_TRACER default n help diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c index 459912ca72e0..12e5f108e242 100644 --- a/kernel/trace/fgraph.c +++ b/kernel/trace/fgraph.c @@ -752,8 +752,8 @@ int function_graph_enter_ops(unsigned long ret, unsigned long func, /* Retrieve a function return address to the trace stack on thread info.*/ static struct ftrace_ret_stack * -ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, - unsigned long frame_pointer, int *index) +ftrace_pop_return_trace(unsigned long *ret, unsigned long frame_pointer, + int *index) { struct ftrace_ret_stack *ret_stack; @@ -798,10 +798,6 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, *index += FGRAPH_RET_INDEX; *ret = ret_stack->ret; - trace->func = ret_stack->func; - trace->calltime = ret_stack->calltime; - trace->overrun = atomic_read(¤t->trace_overrun); - trace->depth = current->curr_ret_depth; /* * We still want to trace interrupts coming in if * max_depth is set to 1. Make sure the decrement is @@ -840,21 +836,42 @@ static struct notifier_block ftrace_suspend_notifier = { /* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */ struct fgraph_ret_regs; +static void fgraph_call_retfunc(struct ftrace_regs *fregs, + struct fgraph_ret_regs *ret_regs, + struct ftrace_ret_stack *ret_stack, + struct fgraph_ops *gops) +{ + struct ftrace_graph_ret trace; + + trace.func = ret_stack->func; + trace.calltime = ret_stack->calltime; + trace.overrun = atomic_read(¤t->trace_overrun); + trace.depth = current->curr_ret_depth; + trace.rettime = trace_clock_local(); +#ifdef CONFIG_FUNCTION_GRAPH_RETVAL + if (fregs) + trace.retval = ftrace_regs_get_return_value(fregs); + else + trace.retval = fgraph_ret_regs_return_value(ret_regs); +#endif + gops->retfunc(&trace, gops); +} + /* * Send the trace to the ring-buffer. * @return the original return address. */ -static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs, +static unsigned long __ftrace_return_to_handler(struct ftrace_regs *fregs, + struct fgraph_ret_regs *ret_regs, unsigned long frame_pointer) { struct ftrace_ret_stack *ret_stack; - struct ftrace_graph_ret trace; unsigned long bitmap; unsigned long ret; int index; int i; - ret_stack = ftrace_pop_return_trace(&trace, &ret, frame_pointer, &index); + ret_stack = ftrace_pop_return_trace(&ret, frame_pointer, &index); if (unlikely(!ret_stack)) { ftrace_graph_stop(); @@ -863,10 +880,8 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs return (unsigned long)panic; } - trace.rettime = trace_clock_local(); -#ifdef CONFIG_FUNCTION_GRAPH_RETVAL - trace.retval = fgraph_ret_regs_return_value(ret_regs); -#endif + if (fregs) + ftrace_regs_set_instruction_pointer(fregs, ret); bitmap = get_fgraph_index_bitmap(current, index); for (i = 0; i < FGRAPH_ARRAY_SIZE; i++) { @@ -877,7 +892,10 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs if (gops == &fgraph_stub) continue; - gops->retfunc(&trace, gops); + if (gops->retregfunc) + gops->retregfunc(ret_stack->func, ret, fregs, gops); + else + fgraph_call_retfunc(fregs, ret_regs, ret_stack, gops); } /* @@ -892,20 +910,22 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs return ret; } -/* - * After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can - * leave only ftrace_return_to_handler(ret_regs). - */ -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler(struct ftrace_regs *fregs) +{ + return __ftrace_return_to_handler(fregs, NULL, + ftrace_regs_get_frame_pointer(fregs)); +} +#elif defined(CONFIG_HAVE_FUNCTION_GRAPH_RETVAL) unsigned long ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs) { - return __ftrace_return_to_handler(ret_regs, + return __ftrace_return_to_handler(NULL, ret_regs, fgraph_ret_regs_frame_pointer(ret_regs)); } #else unsigned long ftrace_return_to_handler(unsigned long frame_pointer) { - return __ftrace_return_to_handler(NULL, frame_pointer); + return __ftrace_return_to_handler(NULL, NULL, frame_pointer); } #endif @@ -1262,9 +1282,15 @@ int register_ftrace_graph(struct fgraph_ops *gops) int ret = 0; int i; - if (gops->entryfunc && gops->entryregfunc) + if ((gops->entryfunc && gops->entryregfunc) || + (gops->retfunc && gops->retregfunc)) return -EINVAL; +#ifndef CONFIG_HAVE_FUNCTION_GRAPH_FREGS + if (gops->retregfunc) + return -EOPNOTSUPP; +#endif + mutex_lock(&ftrace_lock); if (!gops->ops.func) {