tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph
Subsystems (16)
Masami Hiramatsu (Google)
maintainerv14 cover tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph
Hi,
Here is the 14th version of the series to re-implement the fprobe on function-graph tracer. The previous version is;
https://lore.kernel.org/all/172398527264.293426.2050093948411376857.stgit@devnote2/
This version is based on trace-v6.11-rc6.
I dropped already committed bugfix patch, also add RISC-V support of ftrace_regs APIs(ftrace_regs_get_return_address() and ftrace_partial_regs()).
Overview
--------
This series rewrites the fprobe on this function-graph. The purposes of this change are;
1) Remove dependency of the rethook from fprobe so that we can reduce the return hook code and shadow stack.
2) Make 'ftrace_regs' the common trace interface for the function boundary.
1) Currently we have 2(or 3) different function return hook codes, the function-graph tracer and rethook (and legacy kretprobe). But since this is redundant and needs double maintenance cost, I would like to unify those. From the user's viewpoint, function- graph tracer is very useful to grasp the execution path. For this purpose, it is hard to use the rethook in the function-graph tracer, but the opposite is possible. (Strictly speaking, kretprobe can not use it because it requires 'pt_regs' for historical reasons.)
2) Now the fprobe provides the 'pt_regs' for its handler, but that is wrong for the function entry and exit. Moreover, depending on the architecture, there is no way to accurately reproduce 'pt_regs' outside of interrupt or exception handlers. This means fprobe should not use 'pt_regs' because it does not use such exceptions. (Conversely, kprobe should use 'pt_regs' because it is an abstract interface of the software breakpoint exception.)
This series changes fprobe to use function-graph tracer for tracing function entry and exit, instead of mixture of ftrace and rethook. Unlike the rethook which is a per-task list of system-wide allocated nodes, the function graph's ret_stack is a per-task shadow stack. Thus it does not need to set 'nr_maxactive' (which is the number of pre-allocated nodes).
Also the handlers will get the 'ftrace_regs' instead of 'pt_regs'. Since eBPF mulit_kprobe/multi_kretprobe events still use 'pt_regs' as their register interface, this changes it to convert 'ftrace_regs' to 'pt_regs'. Of course this conversion makes an incomplete 'pt_regs', so users must access only registers for function parameters or return value.
Design
------
Instead of using ftrace's function entry hook directly, the new fprobe is built on top of the function-graph's entry and return callbacks with 'ftrace_regs'.
Since the fprobe requires access to 'ftrace_regs', the architecture must support CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS and CONFIG_HAVE_FTRACE_GRAPH_FUNC, which enables to call function-graph entry callback with 'ftrace_regs', and also CONFIG_HAVE_FUNCTION_GRAPH_FREGS, which passes the ftrace_regs to return_to_handler.
All fprobes share a single function-graph ops (means shares a common ftrace filter) similar to the kprobe-on-ftrace. This needs another layer to find corresponding fprobe in the common function-graph callbacks, but has much better scalability, since the number of registered function-graph ops is limited.
In the entry callback, the fprobe runs its entry_handler and saves the address of 'fprobe' on the function-graph's shadow stack as data. The return callback decodes the data to get the 'fprobe' address, and runs the exit_handler.
The fprobe introduces two hash-tables, one is for entry callback which searches fprobes related to the given function address passed by entry callback. The other is for a return callback which checks if the given 'fprobe' data structure pointer is still valid. Note that it is possible to unregister fprobe before the return callback runs. Thus the address validation must be done before using it in the return callback.
Download
--------
This series can be applied against the ftrace/for-next branch in linux-trace tree.
This series can also be found below branch.
https://git.kernel.org/pub/scm/linux/kernel/git/mhiramat/linux.git/log/?h=topic/fprobe-on-fgraph
Thank you,
---
Masami Hiramatsu (Google) (19):
tracing: Add a comment about ftrace_regs definition
tracing: Rename ftrace_regs_return_value to ftrace_regs_get_return_value
function_graph: Pass ftrace_regs to entryfunc
function_graph: Replace fgraph_ret_regs with ftrace_regs
function_graph: Pass ftrace_regs to retfunc
fprobe: Use ftrace_regs in fprobe entry handler
fprobe: Use ftrace_regs in fprobe exit handler
tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
tracing: Add ftrace_fill_perf_regs() for perf event tracing/fprobe: Enable fprobe events with CONFIG_DYNAMIC_FTRACE_WITH_ARGS
bpf: Enable kprobe_multi feature if CONFIG_FPROBE is enabled
ftrace: Add CONFIG_HAVE_FTRACE_GRAPH_FUNC
fprobe: Rewrite fprobe on function-graph tracer
tracing: Fix function timing profiler to initialize hashtable tracing/fprobe: Remove nr_maxactive from fprobe
selftests: ftrace: Remove obsolate maxactive syntax check selftests/ftrace: Add a test case for repeating register/unregister fprobe
Documentation: probes: Update fprobe on function-graph tracer
fgraph: Skip recording calltime/rettime if it is not nneeded
Documentation/trace/fprobe.rst | 42 + arch/arm64/Kconfig | 2 arch/arm64/include/asm/ftrace.h | 47 + arch/arm64/kernel/asm-offsets.c | 12 arch/arm64/kernel/entry-ftrace.S | 32 + arch/arm64/kernel/ftrace.c | 20 + arch/loongarch/Kconfig | 4 arch/loongarch/include/asm/ftrace.h | 32 - arch/loongarch/kernel/asm-offsets.c | 12 arch/loongarch/kernel/ftrace_dyn.c | 10 arch/loongarch/kernel/mcount.S | 17 - arch/loongarch/kernel/mcount_dyn.S | 14 arch/powerpc/Kconfig | 1 arch/powerpc/include/asm/ftrace.h | 15 arch/powerpc/kernel/trace/ftrace.c | 2 arch/powerpc/kernel/trace/ftrace_64_pg.c | 10 arch/riscv/Kconfig | 3 arch/riscv/include/asm/ftrace.h | 43 + arch/riscv/kernel/ftrace.c | 17 + arch/riscv/kernel/mcount.S | 24 - arch/s390/Kconfig | 3 arch/s390/include/asm/ftrace.h | 39 + arch/s390/kernel/asm-offsets.c | 6 arch/s390/kernel/mcount.S | 9 arch/x86/Kconfig | 4 arch/x86/include/asm/ftrace.h | 37 + arch/x86/kernel/ftrace.c | 50 +- arch/x86/kernel/ftrace_32.S | 15 arch/x86/kernel/ftrace_64.S | 17 - include/linux/fprobe.h | 57 +- include/linux/ftrace.h | 136 ++++ kernel/trace/Kconfig | 23 + kernel/trace/bpf_trace.c | 19 - kernel/trace/fgraph.c | 96 ++- kernel/trace/fprobe.c | 638 ++++++++++++++------ kernel/trace/ftrace.c | 10 kernel/trace/trace.h | 6 kernel/trace/trace_fprobe.c | 147 ++--- kernel/trace/trace_functions_graph.c | 10 kernel/trace/trace_irqsoff.c | 6 kernel/trace/trace_probe_tmpl.h | 2 kernel/trace/trace_sched_wakeup.c | 6 kernel/trace/trace_selftest.c | 11 lib/test_fprobe.c | 51 -- samples/fprobe/fprobe_example.c | 4 .../test.d/dynevent/add_remove_fprobe_repeat.tc | 19 + .../ftrace/test.d/dynevent/fprobe_syntax_errors.tc | 4 47 files changed, 1167 insertions(+), 617 deletions(-) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_repeat.tc
--
Masami Hiramatsu (Google) <[email protected]>
Masami Hiramatsu (Google)
maintainerPATCH v14 1/19 tracing: Add a comment about ftrace_regs definition
From: Masami Hiramatsu (Google) <[email protected]>
To clarify what will be expected on ftrace_regs, add a comment to the architecture independent definition of the ftrace_regs.
---
Changes in v8:
- Update that the saved registers depends on the context.
Changes in v3:
- Add instruction pointer
Changes in v2:
- newly added.
--- include/linux/ftrace.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
include/linux/ftrace.h +26 -0
@@ -117,6 +117,32 @@ extern int ftrace_enabled;#ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS +/** +* ftrace_regs - ftrace partial/optimal register set +* +* ftrace_regs represents a group of registers which is used at the +* function entry and exit. There are three types of registers. +* +* - Registers for passing the parameters to callee, including the stack +* pointer. (e.g. rcx, rdx, rdi, rsi, r8, r9 and rsp on x86_64) +* - Registers for passing the return values to caller. +* (e.g. rax and rdx on x86_64) +* - Registers for hooking the function call and return including the +* frame pointer (the frame pointer is architecture/config dependent) +* (e.g. rip, rbp and rsp for x86_64) +* +* Also, architecture dependent fields can be used for internal process. +* (e.g. orig_ax on x86_64) +* +* On the function entry, those registers will be restored except for +* the stack pointer, so that user can change the function parameters +* and instruction pointer (e.g. live patching.) +* On the function exit, only registers which is used for return values +* are restored. +* +* NOTE: user *must not* access regs directly, only do it via APIs, because +* the member can be changed according to the architecture. +*/ struct ftrace_regs {struct pt_regs regs; };
Masami Hiramatsu (Google)
maintainerPATCH v14 2/19 tracing: Rename ftrace_regs_return_value to ftrace_regs_get_return_value
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Rename ftrace_regs_return_value to ftrace_regs_get_return_value as same as other ftrace_regs_get/set_* APIs.
---
Changes in v6:
- Moved to top of the series.
Changes in v3:
- Newly added.
--- arch/loongarch/include/asm/ftrace.h | 2 +- arch/powerpc/include/asm/ftrace.h | 2 +- arch/s390/include/asm/ftrace.h | 2 +- arch/x86/include/asm/ftrace.h | 2 +- include/linux/ftrace.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-)
arch/loongarch/include/asm/ftrace.h +1 -1
@@ -69,7 +69,7 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip) regs_get_kernel_argument(&(fregs)->regs, n)#define ftrace_regs_get_stack_pointer (fregs) \ kernel_stack_pointer(&(fregs)->regs) -#define ftrace_regs_return_value (fregs) \ +#define ftrace_regs_get_return_value (fregs) \ regs_return_value(&(fregs)->regs)#define ftrace_regs_set_return_value (fregs, ret) \ regs_set_return_value(&(fregs)->regs, ret)
arch/powerpc/include/asm/ftrace.h +1 -1
@@ -59,7 +59,7 @@ ftrace_regs_get_instruction_pointer(struct ftrace_regs *fregs) regs_get_kernel_argument(&(fregs)->regs, n)#define ftrace_regs_get_stack_pointer (fregs) \ kernel_stack_pointer(&(fregs)->regs) -#define ftrace_regs_return_value (fregs) \ +#define ftrace_regs_get_return_value (fregs) \ regs_return_value(&(fregs)->regs)#define ftrace_regs_set_return_value (fregs, ret) \ regs_set_return_value(&(fregs)->regs, ret)
arch/s390/include/asm/ftrace.h +1 -1
@@ -83,7 +83,7 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, regs_get_kernel_argument(&(fregs)->regs, n)#define ftrace_regs_get_stack_pointer (fregs) \ kernel_stack_pointer(&(fregs)->regs) -#define ftrace_regs_return_value (fregs) \ +#define ftrace_regs_get_return_value (fregs) \ regs_return_value(&(fregs)->regs)#define ftrace_regs_set_return_value (fregs, ret) \ regs_set_return_value(&(fregs)->regs, ret)
arch/x86/include/asm/ftrace.h +1 -1
@@ -56,7 +56,7 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs) regs_get_kernel_argument(&(fregs)->regs, n)#define ftrace_regs_get_stack_pointer (fregs) \ kernel_stack_pointer(&(fregs)->regs) -#define ftrace_regs_return_value (fregs) \ +#define ftrace_regs_get_return_value (fregs) \ regs_return_value(&(fregs)->regs)#define ftrace_regs_set_return_value (fregs, ret) \ regs_set_return_value(&(fregs)->regs, ret)
include/linux/ftrace.h +1 -1
@@ -183,7 +183,7 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs) regs_get_kernel_argument(ftrace_get_regs(fregs), n)#define ftrace_regs_get_stack_pointer (fregs) \ kernel_stack_pointer(ftrace_get_regs(fregs)) -#define ftrace_regs_return_value (fregs) \ +#define ftrace_regs_get_return_value (fregs) \ regs_return_value(ftrace_get_regs(fregs))#define ftrace_regs_set_return_value (fregs, ret) \ regs_set_return_value(ftrace_get_regs(fregs), ret)
Masami Hiramatsu (Google)
maintainerPATCH v14 3/19 function_graph: Pass ftrace_regs to entryfunc
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Pass ftrace_regs to the fgraph_ops::entryfunc(). If ftrace_regs is not available, it passes a NULL instead. User callback function can access some registers (including return address) via this ftrace_regs.
---
Changes in v11:
- Update for the latest for-next branch.
Changes in v8:
- Just pass ftrace_regs to the handler instead of adding a new entryregfunc.
- Update riscv ftrace_graph_func().
Changes in v3:
- Update for new multiple fgraph.
--- arch/arm64/kernel/ftrace.c | 20 +++++++++++- arch/loongarch/kernel/ftrace_dyn.c | 10 +++++- arch/powerpc/kernel/trace/ftrace.c | 2 + arch/powerpc/kernel/trace/ftrace_64_pg.c | 10 ++++-- arch/riscv/kernel/ftrace.c | 17 ++++++++++ arch/x86/kernel/ftrace.c | 50 +++++++++++++++++++++--------- include/linux/ftrace.h | 18 ++++++++--- kernel/trace/fgraph.c | 23 ++++++++------ kernel/trace/ftrace.c | 3 +- kernel/trace/trace.h | 3 +- kernel/trace/trace_functions_graph.c | 3 +- kernel/trace/trace_irqsoff.c | 3 +- kernel/trace/trace_sched_wakeup.c | 3 +- kernel/trace/trace_selftest.c | 8 +++-- 14 files changed, 128 insertions(+), 45 deletions(-)
arch/arm64/kernel/ftrace.c +19 -1
@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (ip, &fregs->lr, fregs->fp); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->fp; +unsigned long *parent = &fregs->lr; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* Note: +* No protection against faulting at *parent, which may be seen +* on other archs. It's unlikely on AArch64. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, + (void *)frame_pointer, fregs)) { + *parent = return_hooker; + } }#else /*
arch/loongarch/kernel/ftrace_dyn.c +9 -1
@@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *parent = (unsigned long *)®s->regs[1 ]; +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + + old = *parent; -prepare_ftrace_return (ip, (unsigned long *)parent); +if (!function_graph_enter_regs (old, ip,0 , parent, fregs)) + *parent = return_hooker; }#else static int ftrace_modify_graph_caller (bool enable)
arch/powerpc/kernel/trace/ftrace.c +1 -1
@@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,if (bit <0 ) goto out; -if (!function_graph_enter (parent_ip, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent_ip, ip,0 , (unsigned long *)sp, fregs)) parent_ip =ppc_function_entry (return_to_handler);ftrace_test_recursion_unlock (bit);
arch/powerpc/kernel/trace/ftrace_64_pg.c +6 -4
@@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)* in current thread info. Return the address we want to divert to. */ static unsigned long -__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) +__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp, +struct ftrace_regs *fregs) {unsigned long return_hooker;int bit;
@@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp return_hooker =ppc_function_entry (return_to_handler); -if (!function_graph_enter (parent, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent, ip,0 , (unsigned long *)sp, fregs)) parent = return_hooker;ftrace_test_recursion_unlock (bit);
@@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long spvoid ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { - fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, fregs->regs.gpr[1 ]); + fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, + fregs->regs.gpr[1 ], fregs); }#else unsigned long prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) { -return __prepare_ftrace_return (parent, ip, sp); +return __prepare_ftrace_return (parent, ip, sp,NULL ); }#endif #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
arch/riscv/kernel/ftrace.c +16 -1
@@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (&fregs->ra, ip, fregs->s0); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->s0; +unsigned long *parent = &fregs->ra; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* We don't suffer access faults, so no extra fault-recovery assembly +* is needed here. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, parent, fregs)) + *parent = return_hooker; }#else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */ extern void ftrace_graph_call (void );
arch/x86/kernel/ftrace.c +35 -15
72 lines not on this page · raw message
include/linux/ftrace.h +14 -4
@@ -1063,9 +1063,12 @@ struct fgraph_ops;typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *,struct fgraph_ops *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *, -struct fgraph_ops *);/* entry */ +struct fgraph_ops *, +struct ftrace_regs *);/* entry */ -extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, +struct fgraph_ops *gops, +struct ftrace_regs *fregs);bool ftrace_pids_enabled (struct ftrace_ops *ops);#ifdef CONFIG_FUNCTION_GRAPH_TRACER
@@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {extern void return_to_handler (void );extern int -function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp); +function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs); + +static inline int function_graph_enter (unsigned long ret,unsigned long func, +unsigned long fp,unsigned long *retp) +{ +return function_graph_enter_regs (ret, func, fp, retp,NULL ); +}struct ftrace_ret_stack *ftrace_graph_get_ret_stack (struct task_struct *task,int skip);
kernel/trace/fgraph.c +13 -10
@@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset) }/* ftrace_graph_entry set to this to tell some archs to run function graph */ -static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops) +static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) {return 0 ; }
@@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)#endif int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {return 0 ; }
@@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,#endif /* If the caller does not use ftrace, call this function. */ -int function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp) +int function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs) {struct ftrace_graph_ent trace;unsigned long bitmap =0 ;
@@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,if (static_branch_likely (&fgraph_do_direct)) {int save_curr_ret_stack = current->curr_ret_stack; -if (static_call (fgraph_func)(&trace, fgraph_direct_gops)) +if (static_call (fgraph_func)(&trace, fgraph_direct_gops, fregs)) bitmap |=BIT (fgraph_direct_gops->idx);else /* Clear out any saved storage */
@@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func, save_curr_ret_stack = current->curr_ret_stack;if (ftrace_ops_test (&gops->ops, func,NULL ) && - gops->entryfunc (&trace, gops)) + gops->entryfunc (&trace, gops, fregs)) bitmap |=BIT (i);else /* Clear out any saved storage */
@@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,static struct ftrace_ops graph_ops = { .func = ftrace_graph_func, - .flags =FTRACE_OPS_GRAPH_STUB , + .flags =FTRACE_OPS_GRAPH_STUB |FTRACE_OPS_FL_SAVE_ARGS , #ifdef FTRACE_GRAPH_TRAMP_ADDR .trampoline =FTRACE_GRAPH_TRAMP_ADDR , /* trampoline_size is only needed for dynamically allocated tramps */
@@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {void fgraph_init_ops (struct ftrace_ops *dst_ops,struct ftrace_ops *src_ops) { - dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB ; + dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB | +FTRACE_OPS_FL_SAVE_ARGS ;#ifdef CONFIG_DYNAMIC_FTRACE if (src_ops) {
@@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)#ifdef CONFIG_DYNAMIC_FTRACE static int fgraph_pid_func (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {struct trace_array *tr = gops->ops.private;int pid;
@@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,return 0 ; } -return gops->saved_func (trace, gops); +return gops->saved_func (trace, gops, fregs); }void fgraph_update_pid_func (void )
kernel/trace/ftrace.c +2 -1
@@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable) }static int profile_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;
kernel/trace/trace.h +2 -1
@@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter);void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); -int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);void tracing_start_cmdline_record (void );void tracing_stop_cmdline_record (void );
kernel/trace/trace_functions_graph.c +2 -1
@@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void) }int trace_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;
kernel/trace/trace_irqsoff.c +2 -1
@@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set) }static int irqsoff_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;
kernel/trace/trace_sched_wakeup.c +2 -1
@@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set) }static int wakeup_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;
kernel/trace/trace_selftest.c +5 -3
@@ -773,7 +773,8 @@ struct fgraph_fixture { };static __init int store_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;
@@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;/* Wrap the real function entry probe to avoid possible hanging */ static int trace_graph_entry_watchdog (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {/* This is harmlessly racy, we want to approximately detect a hang */ if (unlikely (++graph_hang_thresh >GRAPH_MAX_FUNC_TEST )) {
@@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,return 0 ; } -return trace_graph_entry (trace, gops); +return trace_graph_entry (trace, gops, fregs); }static struct fgraph_ops fgraph_ops__initdata = {
Steven Rostedt
maintainerRe: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
Can I get an Acked-by from the AARCH64 maintainers for this patch?
Thanks!
-- Steve
Quoted 476 lines · On Fri, 13 Sep 2024 00:08:40 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::entryfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v11:
- Update for the latest for-next branch.
Changes in v8:
- Just passftrace_regsto the handler instead of adding a new entryregfunc.
- Update riscvftrace_graph_func().
Changes in v3:
- Update for new multiple fgraph.--- arch/arm64/kernel/ftrace.c | 20 +++++++++++- arch/loongarch/kernel/ftrace_dyn.c | 10 +++++- arch/powerpc/kernel/trace/ftrace.c | 2 + arch/powerpc/kernel/trace/ftrace_64_pg.c | 10 ++++-- arch/riscv/kernel/ftrace.c | 17 ++++++++++ arch/x86/kernel/ftrace.c | 50 +++++++++++++++++++++--------- include/linux/ftrace.h | 18 ++++++++--- kernel/trace/fgraph.c | 23 ++++++++------ kernel/trace/ftrace.c | 3 +- kernel/trace/trace.h | 3 +- kernel/trace/trace_functions_graph.c | 3 +- kernel/trace/trace_irqsoff.c | 3 +- kernel/trace/trace_sched_wakeup.c | 3 +- kernel/trace/trace_selftest.c | 8 +++-- 14 files changed, 128 insertions(+), 45 deletions(-)arch/arm64/kernel/ftrace.c +19
-1@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (ip, &fregs->lr, fregs->fp); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->fp; +unsigned long *parent = &fregs->lr; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* Note: +* No protection against faulting at *parent, which may be seen +* on other archs. It's unlikely on AArch64. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, + (void *)frame_pointer, fregs)) { + *parent = return_hooker; + } }#else /* arch/loongarch/kernel/ftrace_dyn.c +9
-1@@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *parent = (unsigned long *)®s->regs[1 ]; +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + + old = *parent; -prepare_ftrace_return (ip, (unsigned long *)parent); +if (!function_graph_enter_regs (old, ip,0 , parent, fregs)) + *parent = return_hooker; }#else static int ftrace_modify_graph_caller (bool enable)arch/powerpc/kernel/trace/ftrace.c +1
-1@@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,if (bit <0 ) goto out; -if (!function_graph_enter (parent_ip, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent_ip, ip,0 , (unsigned long *)sp, fregs)) parent_ip =ppc_function_entry (return_to_handler);ftrace_test_recursion_unlock (bit);arch/powerpc/kernel/trace/ftrace_64_pg.c +6
-4@@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)* in current thread info. Return the address we want to divert to. */ static unsigned long -__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) +__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp, +struct ftrace_regs *fregs) {unsigned long return_hooker;int bit;@@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp return_hooker =ppc_function_entry (return_to_handler); -if (!function_graph_enter (parent, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent, ip,0 , (unsigned long *)sp, fregs)) parent = return_hooker;ftrace_test_recursion_unlock (bit);@@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long spvoid ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { - fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, fregs->regs.gpr[1 ]); + fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, + fregs->regs.gpr[1 ], fregs); }#else unsigned long prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) { -return __prepare_ftrace_return (parent, ip, sp); +return __prepare_ftrace_return (parent, ip, sp,NULL ); }#endif #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ arch/riscv/kernel/ftrace.c +16
-1@@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (&fregs->ra, ip, fregs->s0); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->s0; +unsigned long *parent = &fregs->ra; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* We don't suffer access faults, so no extra fault-recovery assembly +* is needed here. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, parent, fregs)) + *parent = return_hooker; }#else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */ extern void ftrace_graph_call (void );arch/x86/kernel/ftrace.c +35
-15@@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void) }#endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ -/* -* Hook the return address and push it in the stack of return addrs -* in current thread info. -*/ -void prepare_ftrace_return (unsigned long ip,unsigned long *parent, -unsigned long frame_pointer) +static inline bool skip_ftrace_return (void ) { -unsigned long return_hooker = (unsigned long )&return_to_handler; -int bit; -/* * When resuming from suspend-to-ram, this function can be indirectly * called from early CPU startup code while the CPU is in real mode, @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,* This check isn't as accurate as virt_addr_valid(), but it should be * good enough for this purpose, and it's fast. */ -if (unlikely ((long )__builtin_frame_address (0 ) >=0 )) -return ; +if ((long )__builtin_frame_address (0 ) >=0 ) +return true; -if (unlikely (ftrace_graph_is_dead ())) -return ; +if (ftrace_graph_is_dead ()) +return true; + +if (atomic_read (¤t->tracing_graph_pause)) +return true; +return false; +} + +/* +* Hook the return address and push it in the stack of return addrs +* in current thread info. +*/ +void prepare_ftrace_return (unsigned long ip,unsigned long *parent, +unsigned long frame_pointer) +{ +unsigned long return_hooker = (unsigned long )&return_to_handler; +int bit; -if (unlikely (atomic_read (¤t->tracing_graph_pause))) +if (unlikely (skip_ftrace_return ()))return ; bit =ftrace_test_recursion_trylock (ip, *parent);@@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *stack = (unsigned long *)kernel_stack_pointer (regs); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long *parent = (unsigned long *)stack; +int bit; -prepare_ftrace_return (ip, (unsigned long *)stack,0 ); +if (unlikely (skip_ftrace_return ())) +return ; + + bit =ftrace_test_recursion_trylock (ip, *parent); +if (bit <0 ) +return ; + +if (!function_graph_enter_regs (*parent, ip,0 , parent, fregs)) + *parent = return_hooker; + +ftrace_test_recursion_unlock (bit); }#endif include/linux/ftrace.h +14
-4@@ -1063,9 +1063,12 @@ struct fgraph_ops;typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *,struct fgraph_ops *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *, -struct fgraph_ops *);/* entry */ +struct fgraph_ops *, +struct ftrace_regs *);/* entry */ -extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, +struct fgraph_ops *gops, +struct ftrace_regs *fregs);bool ftrace_pids_enabled (struct ftrace_ops *ops);#ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {extern void return_to_handler (void );extern int -function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp); +function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs); + +static inline int function_graph_enter (unsigned long ret,unsigned long func, +unsigned long fp,unsigned long *retp) +{ +return function_graph_enter_regs (ret, func, fp, retp,NULL ); +}struct ftrace_ret_stack *ftrace_graph_get_ret_stack (struct task_struct *task,int skip);kernel/trace/fgraph.c +13
-10@@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset) }/* ftrace_graph_entry set to this to tell some archs to run function graph */ -static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops) +static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) {return 0 ; }@@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)#endif int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {return 0 ; }@@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,#endif /* If the caller does not use ftrace, call this function. */ -int function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp) +int function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs) {struct ftrace_graph_ent trace;unsigned long bitmap =0 ;@@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,if (static_branch_likely (&fgraph_do_direct)) {int save_curr_ret_stack = current->curr_ret_stack; -if (static_call (fgraph_func)(&trace, fgraph_direct_gops)) +if (static_call (fgraph_func)(&trace, fgraph_direct_gops, fregs)) bitmap |=BIT (fgraph_direct_gops->idx);else /* Clear out any saved storage */ @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func, save_curr_ret_stack = current->curr_ret_stack;if (ftrace_ops_test (&gops->ops, func,NULL ) && - gops->entryfunc (&trace, gops)) + gops->entryfunc (&trace, gops, fregs)) bitmap |=BIT (i);else /* Clear out any saved storage */ @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,static struct ftrace_ops graph_ops = { .func = ftrace_graph_func, - .flags =FTRACE_OPS_GRAPH_STUB , + .flags =FTRACE_OPS_GRAPH_STUB |FTRACE_OPS_FL_SAVE_ARGS , #ifdef FTRACE_GRAPH_TRAMP_ADDR .trampoline =FTRACE_GRAPH_TRAMP_ADDR , /* trampoline_size is only needed for dynamically allocated tramps */@@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {void fgraph_init_ops (struct ftrace_ops *dst_ops,struct ftrace_ops *src_ops) { - dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB ; + dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB | +FTRACE_OPS_FL_SAVE_ARGS ;#ifdef CONFIG_DYNAMIC_FTRACE if (src_ops) {@@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)#ifdef CONFIG_DYNAMIC_FTRACE static int fgraph_pid_func (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {struct trace_array *tr = gops->ops.private;int pid;@@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,return 0 ; } -return gops->saved_func (trace, gops); +return gops->saved_func (trace, gops, fregs); }void fgraph_update_pid_func (void )kernel/trace/ftrace.c +2
-1@@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable) }static int profile_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;kernel/trace/trace.h +2
-1@@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter);void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); -int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);void tracing_start_cmdline_record (void );void tracing_stop_cmdline_record (void );kernel/trace/trace_functions_graph.c +2
-1@@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void) }int trace_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;kernel/trace/trace_irqsoff.c +2
-1@@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set) }static int irqsoff_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set) }static int wakeup_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +5
-3@@ -773,7 +773,8 @@ struct fgraph_fixture { };static __init int store_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;@@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;/* Wrap the real function entry probe to avoid possible hanging */ static int trace_graph_entry_watchdog (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {/* This is harmlessly racy, we want to approximately detect a hang */ if (unlikely (++graph_hang_thresh >GRAPH_MAX_FUNC_TEST )) {@@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,return 0 ; } -return trace_graph_entry (trace, gops); +return trace_graph_entry (trace, gops, fregs); }static struct fgraph_ops fgraph_ops__initdata = {
Will Deacon
maintainerRe: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
Quoted 1 line · Steven Rostedt
Can I get an Acked-by from the AARCH64 maintainers for this patch?
Sorry, I wasn't CC'd on the thread, so I missed this.
Quoted 8 lines
Quoted 5 lines · On Fri, 13 Sep 2024 00:08:40 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::entryfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.
Under which circumstances is 'ftrace_regs' NULL?
The arm64 implementation of ftrace_graph_func() is:
Quoted 11 lines
arch/arm64/kernel/ftrace.c void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, · line 485
Quoted 11 lines
arch/arm64/kernel/ftrace.c +2
-1@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (ip, &fregs->lr, fregs->fp); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->fp;
Which dereferences the unchecked pointer here ^^.
Will
Steven Rostedt
maintainerRe: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
Quoted 4 lines · On Tue, 17 Sep 2024 09:26:25 +0100 Will Deacon <[email protected]> wrote:
Quoted 1 line · Steven Rostedt
Can I get an Acked-by from the AARCH64 maintainers for this patch?
Sorry, I wasn't CC'd on the thread, so I missed this.
Here's the lore link that starts it all:
https://lore.kernel.org/all/172615368656.133222.2336770908714920670.stgit@devnote2/
There's also a v15:
https://lore.kernel.org/linux-trace-kernel/172639136989.366111.11359590127009702129.stgit@devnote2/
Quoted 11 lines
Quoted 9 lines
Quoted 5 lines · On Fri, 13 Sep 2024 00:08:40 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::entryfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Under which circumstances is '
ftrace_regs' NULL?
When the arch does not define: HAVE_DYNAMIC_FTRACE_WITH_ARGS or HAVE_DYNAMIC_FTRACE_WITH_REGS
If HAVE_DYNAMIC_FTRACE_WITH_REGS is defined but not the WITH_ARGS, and the ops used to register the function callback does not set FTRACE_OPS_FL_SAVE_REGS.
Otherwise it should be set.
Quoted 16 lines
The arm64 implementation of
ftrace_graph_func()is:Quoted 11 lines
arch/arm64/kernel/ftrace.c void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, · line 485
Quoted 11 lines
arch/arm64/kernel/ftrace.c +2
-1@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (ip, &fregs->lr, fregs->fp); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->fp;Which dereferences the unchecked pointer here ^^.
Just above the visible portion of the patch, we have:
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct ftrace_regs *fregs)
{
Which means fregs will be available. The "WITH_ARGS" config tells us that the arch will supply the function with valid fregs to the ftrace callback (which this is).
-- Steve
Masami Hiramatsu (Google)
maintainerRe: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
Quoted 28 lines · On Mon, 30 Sep 2024 14:46:59 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 4 lines · On Tue, 17 Sep 2024 09:26:25 +0100 Will Deacon <[email protected]> wrote:
Quoted 1 line · Steven Rostedt
Can I get an Acked-by from the AARCH64 maintainers for this patch?
Sorry, I wasn't CC'd on the thread, so I missed this.
Here's the lore link that starts it all:
https://lore.kernel.org/all/172615368656.133222.2336770908714920670.stgit@devnote2/
There's also a v15:
https://lore.kernel.org/linux-trace-kernel/172639136989.366111.11359590127009702129.stgit@devnote2/Quoted 11 lines
Quoted 9 lines
Quoted 5 lines · On Fri, 13 Sep 2024 00:08:40 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::entryfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Under which circumstances is '
ftrace_regs' NULL?
Quoted 7 lines
When the arch does not define:
HAVE_DYNAMIC_FTRACE_WITH_ARGSorHAVE_DYNAMIC_FTRACE_WITH_REGSIf
HAVE_DYNAMIC_FTRACE_WITH_REGSis defined but not theWITH_ARGS, and the ops used to register the function callback does not setFTRACE_OPS_FL_SAVE_REGS.Otherwise it should be set.
I will add this condition in the description in next version.
Thank you,
Quoted 31 lines
Quoted 4 lines
The arm64 implementation of
ftrace_graph_func()is:Quoted 13 lines
Quoted 11 lines
arch/arm64/kernel/ftrace.c void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent, · line 485
Quoted 11 lines
arch/arm64/kernel/ftrace.c +2
-1@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (ip, &fregs->lr, fregs->fp); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->fp;Which dereferences the unchecked pointer here ^^.
Just above the visible portion of the patch, we have:
#ifdef
CONFIG_DYNAMIC_FTRACE_WITH_ARGS
void ftrace_graph_func(unsigned long ip, unsigned longparent_ip,
structftrace_ops*op, structftrace_regs*fregs)
{Which means fregs will be available. The "
WITH_ARGS" config tells us that the arch will supply the function with valid fregs to the ftrace callback (which this is).-- Steve
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Steven Rostedt
maintainerRe: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the LOONGARCH maintainers for this patch?
Thanks!
-- Steve
Quoted 476 lines · On Fri, 13 Sep 2024 00:08:40 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::entryfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v11:
- Update for the latest for-next branch.
Changes in v8:
- Just passftrace_regsto the handler instead of adding a new entryregfunc.
- Update riscvftrace_graph_func().
Changes in v3:
- Update for new multiple fgraph.--- arch/arm64/kernel/ftrace.c | 20 +++++++++++- arch/loongarch/kernel/ftrace_dyn.c | 10 +++++- arch/powerpc/kernel/trace/ftrace.c | 2 + arch/powerpc/kernel/trace/ftrace_64_pg.c | 10 ++++-- arch/riscv/kernel/ftrace.c | 17 ++++++++++ arch/x86/kernel/ftrace.c | 50 +++++++++++++++++++++--------- include/linux/ftrace.h | 18 ++++++++--- kernel/trace/fgraph.c | 23 ++++++++------ kernel/trace/ftrace.c | 3 +- kernel/trace/trace.h | 3 +- kernel/trace/trace_functions_graph.c | 3 +- kernel/trace/trace_irqsoff.c | 3 +- kernel/trace/trace_sched_wakeup.c | 3 +- kernel/trace/trace_selftest.c | 8 +++-- 14 files changed, 128 insertions(+), 45 deletions(-)arch/arm64/kernel/ftrace.c +19
-1@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (ip, &fregs->lr, fregs->fp); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->fp; +unsigned long *parent = &fregs->lr; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* Note: +* No protection against faulting at *parent, which may be seen +* on other archs. It's unlikely on AArch64. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, + (void *)frame_pointer, fregs)) { + *parent = return_hooker; + } }#else /* arch/loongarch/kernel/ftrace_dyn.c +9
-1@@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *parent = (unsigned long *)®s->regs[1 ]; +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + + old = *parent; -prepare_ftrace_return (ip, (unsigned long *)parent); +if (!function_graph_enter_regs (old, ip,0 , parent, fregs)) + *parent = return_hooker; }#else static int ftrace_modify_graph_caller (bool enable)arch/powerpc/kernel/trace/ftrace.c +1
-1@@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,if (bit <0 ) goto out; -if (!function_graph_enter (parent_ip, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent_ip, ip,0 , (unsigned long *)sp, fregs)) parent_ip =ppc_function_entry (return_to_handler);ftrace_test_recursion_unlock (bit);arch/powerpc/kernel/trace/ftrace_64_pg.c +6
-4@@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)* in current thread info. Return the address we want to divert to. */ static unsigned long -__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) +__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp, +struct ftrace_regs *fregs) {unsigned long return_hooker;int bit;@@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp return_hooker =ppc_function_entry (return_to_handler); -if (!function_graph_enter (parent, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent, ip,0 , (unsigned long *)sp, fregs)) parent = return_hooker;ftrace_test_recursion_unlock (bit);@@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long spvoid ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { - fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, fregs->regs.gpr[1 ]); + fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, + fregs->regs.gpr[1 ], fregs); }#else unsigned long prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) { -return __prepare_ftrace_return (parent, ip, sp); +return __prepare_ftrace_return (parent, ip, sp,NULL ); }#endif #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ arch/riscv/kernel/ftrace.c +16
-1@@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (&fregs->ra, ip, fregs->s0); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->s0; +unsigned long *parent = &fregs->ra; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* We don't suffer access faults, so no extra fault-recovery assembly +* is needed here. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, parent, fregs)) + *parent = return_hooker; }#else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */ extern void ftrace_graph_call (void );arch/x86/kernel/ftrace.c +35
-15@@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void) }#endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ -/* -* Hook the return address and push it in the stack of return addrs -* in current thread info. -*/ -void prepare_ftrace_return (unsigned long ip,unsigned long *parent, -unsigned long frame_pointer) +static inline bool skip_ftrace_return (void ) { -unsigned long return_hooker = (unsigned long )&return_to_handler; -int bit; -/* * When resuming from suspend-to-ram, this function can be indirectly * called from early CPU startup code while the CPU is in real mode, @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,* This check isn't as accurate as virt_addr_valid(), but it should be * good enough for this purpose, and it's fast. */ -if (unlikely ((long )__builtin_frame_address (0 ) >=0 )) -return ; +if ((long )__builtin_frame_address (0 ) >=0 ) +return true; -if (unlikely (ftrace_graph_is_dead ())) -return ; +if (ftrace_graph_is_dead ()) +return true; + +if (atomic_read (¤t->tracing_graph_pause)) +return true; +return false; +} + +/* +* Hook the return address and push it in the stack of return addrs +* in current thread info. +*/ +void prepare_ftrace_return (unsigned long ip,unsigned long *parent, +unsigned long frame_pointer) +{ +unsigned long return_hooker = (unsigned long )&return_to_handler; +int bit; -if (unlikely (atomic_read (¤t->tracing_graph_pause))) +if (unlikely (skip_ftrace_return ()))return ; bit =ftrace_test_recursion_trylock (ip, *parent);@@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *stack = (unsigned long *)kernel_stack_pointer (regs); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long *parent = (unsigned long *)stack; +int bit; -prepare_ftrace_return (ip, (unsigned long *)stack,0 ); +if (unlikely (skip_ftrace_return ())) +return ; + + bit =ftrace_test_recursion_trylock (ip, *parent); +if (bit <0 ) +return ; + +if (!function_graph_enter_regs (*parent, ip,0 , parent, fregs)) + *parent = return_hooker; + +ftrace_test_recursion_unlock (bit); }#endif include/linux/ftrace.h +14
-4@@ -1063,9 +1063,12 @@ struct fgraph_ops;typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *,struct fgraph_ops *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *, -struct fgraph_ops *);/* entry */ +struct fgraph_ops *, +struct ftrace_regs *);/* entry */ -extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, +struct fgraph_ops *gops, +struct ftrace_regs *fregs);bool ftrace_pids_enabled (struct ftrace_ops *ops);#ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {extern void return_to_handler (void );extern int -function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp); +function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs); + +static inline int function_graph_enter (unsigned long ret,unsigned long func, +unsigned long fp,unsigned long *retp) +{ +return function_graph_enter_regs (ret, func, fp, retp,NULL ); +}struct ftrace_ret_stack *ftrace_graph_get_ret_stack (struct task_struct *task,int skip);kernel/trace/fgraph.c +13
-10@@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset) }/* ftrace_graph_entry set to this to tell some archs to run function graph */ -static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops) +static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) {return 0 ; }@@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)#endif int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {return 0 ; }@@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,#endif /* If the caller does not use ftrace, call this function. */ -int function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp) +int function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs) {struct ftrace_graph_ent trace;unsigned long bitmap =0 ;@@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,if (static_branch_likely (&fgraph_do_direct)) {int save_curr_ret_stack = current->curr_ret_stack; -if (static_call (fgraph_func)(&trace, fgraph_direct_gops)) +if (static_call (fgraph_func)(&trace, fgraph_direct_gops, fregs)) bitmap |=BIT (fgraph_direct_gops->idx);else /* Clear out any saved storage */ @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func, save_curr_ret_stack = current->curr_ret_stack;if (ftrace_ops_test (&gops->ops, func,NULL ) && - gops->entryfunc (&trace, gops)) + gops->entryfunc (&trace, gops, fregs)) bitmap |=BIT (i);else /* Clear out any saved storage */ @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,static struct ftrace_ops graph_ops = { .func = ftrace_graph_func, - .flags =FTRACE_OPS_GRAPH_STUB , + .flags =FTRACE_OPS_GRAPH_STUB |FTRACE_OPS_FL_SAVE_ARGS , #ifdef FTRACE_GRAPH_TRAMP_ADDR .trampoline =FTRACE_GRAPH_TRAMP_ADDR , /* trampoline_size is only needed for dynamically allocated tramps */@@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {void fgraph_init_ops (struct ftrace_ops *dst_ops,struct ftrace_ops *src_ops) { - dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB ; + dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB | +FTRACE_OPS_FL_SAVE_ARGS ;#ifdef CONFIG_DYNAMIC_FTRACE if (src_ops) {@@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)#ifdef CONFIG_DYNAMIC_FTRACE static int fgraph_pid_func (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {struct trace_array *tr = gops->ops.private;int pid;@@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,return 0 ; } -return gops->saved_func (trace, gops); +return gops->saved_func (trace, gops, fregs); }void fgraph_update_pid_func (void )kernel/trace/ftrace.c +2
-1@@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable) }static int profile_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;kernel/trace/trace.h +2
-1@@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter);void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); -int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);void tracing_start_cmdline_record (void );void tracing_stop_cmdline_record (void );kernel/trace/trace_functions_graph.c +2
-1@@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void) }int trace_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;kernel/trace/trace_irqsoff.c +2
-1@@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set) }static int irqsoff_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set) }static int wakeup_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +5
-3@@ -773,7 +773,8 @@ struct fgraph_fixture { };static __init int store_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;@@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;/* Wrap the real function entry probe to avoid possible hanging */ static int trace_graph_entry_watchdog (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {/* This is harmlessly racy, we want to approximately detect a hang */ if (unlikely (++graph_hang_thresh >GRAPH_MAX_FUNC_TEST )) {@@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,return 0 ; } -return trace_graph_entry (trace, gops); +return trace_graph_entry (trace, gops, fregs); }static struct fgraph_ops fgraph_ops__initdata = {
Steven Rostedt
maintainerRe: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the POWERPC maintainers for this patch?
Thanks!
-- Steve
Quoted 476 lines · On Fri, 13 Sep 2024 00:08:40 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::entryfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v11:
- Update for the latest for-next branch.
Changes in v8:
- Just passftrace_regsto the handler instead of adding a new entryregfunc.
- Update riscvftrace_graph_func().
Changes in v3:
- Update for new multiple fgraph.--- arch/arm64/kernel/ftrace.c | 20 +++++++++++- arch/loongarch/kernel/ftrace_dyn.c | 10 +++++- arch/powerpc/kernel/trace/ftrace.c | 2 + arch/powerpc/kernel/trace/ftrace_64_pg.c | 10 ++++-- arch/riscv/kernel/ftrace.c | 17 ++++++++++ arch/x86/kernel/ftrace.c | 50 +++++++++++++++++++++--------- include/linux/ftrace.h | 18 ++++++++--- kernel/trace/fgraph.c | 23 ++++++++------ kernel/trace/ftrace.c | 3 +- kernel/trace/trace.h | 3 +- kernel/trace/trace_functions_graph.c | 3 +- kernel/trace/trace_irqsoff.c | 3 +- kernel/trace/trace_sched_wakeup.c | 3 +- kernel/trace/trace_selftest.c | 8 +++-- 14 files changed, 128 insertions(+), 45 deletions(-)arch/arm64/kernel/ftrace.c +19
-1@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (ip, &fregs->lr, fregs->fp); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->fp; +unsigned long *parent = &fregs->lr; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* Note: +* No protection against faulting at *parent, which may be seen +* on other archs. It's unlikely on AArch64. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, + (void *)frame_pointer, fregs)) { + *parent = return_hooker; + } }#else /* arch/loongarch/kernel/ftrace_dyn.c +9
-1@@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *parent = (unsigned long *)®s->regs[1 ]; +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + + old = *parent; -prepare_ftrace_return (ip, (unsigned long *)parent); +if (!function_graph_enter_regs (old, ip,0 , parent, fregs)) + *parent = return_hooker; }#else static int ftrace_modify_graph_caller (bool enable)arch/powerpc/kernel/trace/ftrace.c +1
-1@@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,if (bit <0 ) goto out; -if (!function_graph_enter (parent_ip, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent_ip, ip,0 , (unsigned long *)sp, fregs)) parent_ip =ppc_function_entry (return_to_handler);ftrace_test_recursion_unlock (bit);arch/powerpc/kernel/trace/ftrace_64_pg.c +6
-4@@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)* in current thread info. Return the address we want to divert to. */ static unsigned long -__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) +__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp, +struct ftrace_regs *fregs) {unsigned long return_hooker;int bit;@@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp return_hooker =ppc_function_entry (return_to_handler); -if (!function_graph_enter (parent, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent, ip,0 , (unsigned long *)sp, fregs)) parent = return_hooker;ftrace_test_recursion_unlock (bit);@@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long spvoid ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { - fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, fregs->regs.gpr[1 ]); + fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, + fregs->regs.gpr[1 ], fregs); }#else unsigned long prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) { -return __prepare_ftrace_return (parent, ip, sp); +return __prepare_ftrace_return (parent, ip, sp,NULL ); }#endif #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ arch/riscv/kernel/ftrace.c +16
-1@@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (&fregs->ra, ip, fregs->s0); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->s0; +unsigned long *parent = &fregs->ra; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* We don't suffer access faults, so no extra fault-recovery assembly +* is needed here. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, parent, fregs)) + *parent = return_hooker; }#else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */ extern void ftrace_graph_call (void );arch/x86/kernel/ftrace.c +35
-15@@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void) }#endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ -/* -* Hook the return address and push it in the stack of return addrs -* in current thread info. -*/ -void prepare_ftrace_return (unsigned long ip,unsigned long *parent, -unsigned long frame_pointer) +static inline bool skip_ftrace_return (void ) { -unsigned long return_hooker = (unsigned long )&return_to_handler; -int bit; -/* * When resuming from suspend-to-ram, this function can be indirectly * called from early CPU startup code while the CPU is in real mode, @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,* This check isn't as accurate as virt_addr_valid(), but it should be * good enough for this purpose, and it's fast. */ -if (unlikely ((long )__builtin_frame_address (0 ) >=0 )) -return ; +if ((long )__builtin_frame_address (0 ) >=0 ) +return true; -if (unlikely (ftrace_graph_is_dead ())) -return ; +if (ftrace_graph_is_dead ()) +return true; + +if (atomic_read (¤t->tracing_graph_pause)) +return true; +return false; +} + +/* +* Hook the return address and push it in the stack of return addrs +* in current thread info. +*/ +void prepare_ftrace_return (unsigned long ip,unsigned long *parent, +unsigned long frame_pointer) +{ +unsigned long return_hooker = (unsigned long )&return_to_handler; +int bit; -if (unlikely (atomic_read (¤t->tracing_graph_pause))) +if (unlikely (skip_ftrace_return ()))return ; bit =ftrace_test_recursion_trylock (ip, *parent);@@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *stack = (unsigned long *)kernel_stack_pointer (regs); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long *parent = (unsigned long *)stack; +int bit; -prepare_ftrace_return (ip, (unsigned long *)stack,0 ); +if (unlikely (skip_ftrace_return ())) +return ; + + bit =ftrace_test_recursion_trylock (ip, *parent); +if (bit <0 ) +return ; + +if (!function_graph_enter_regs (*parent, ip,0 , parent, fregs)) + *parent = return_hooker; + +ftrace_test_recursion_unlock (bit); }#endif include/linux/ftrace.h +14
-4@@ -1063,9 +1063,12 @@ struct fgraph_ops;typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *,struct fgraph_ops *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *, -struct fgraph_ops *);/* entry */ +struct fgraph_ops *, +struct ftrace_regs *);/* entry */ -extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, +struct fgraph_ops *gops, +struct ftrace_regs *fregs);bool ftrace_pids_enabled (struct ftrace_ops *ops);#ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {extern void return_to_handler (void );extern int -function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp); +function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs); + +static inline int function_graph_enter (unsigned long ret,unsigned long func, +unsigned long fp,unsigned long *retp) +{ +return function_graph_enter_regs (ret, func, fp, retp,NULL ); +}struct ftrace_ret_stack *ftrace_graph_get_ret_stack (struct task_struct *task,int skip);kernel/trace/fgraph.c +13
-10@@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset) }/* ftrace_graph_entry set to this to tell some archs to run function graph */ -static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops) +static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) {return 0 ; }@@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)#endif int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {return 0 ; }@@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,#endif /* If the caller does not use ftrace, call this function. */ -int function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp) +int function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs) {struct ftrace_graph_ent trace;unsigned long bitmap =0 ;@@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,if (static_branch_likely (&fgraph_do_direct)) {int save_curr_ret_stack = current->curr_ret_stack; -if (static_call (fgraph_func)(&trace, fgraph_direct_gops)) +if (static_call (fgraph_func)(&trace, fgraph_direct_gops, fregs)) bitmap |=BIT (fgraph_direct_gops->idx);else /* Clear out any saved storage */ @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func, save_curr_ret_stack = current->curr_ret_stack;if (ftrace_ops_test (&gops->ops, func,NULL ) && - gops->entryfunc (&trace, gops)) + gops->entryfunc (&trace, gops, fregs)) bitmap |=BIT (i);else /* Clear out any saved storage */ @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,static struct ftrace_ops graph_ops = { .func = ftrace_graph_func, - .flags =FTRACE_OPS_GRAPH_STUB , + .flags =FTRACE_OPS_GRAPH_STUB |FTRACE_OPS_FL_SAVE_ARGS , #ifdef FTRACE_GRAPH_TRAMP_ADDR .trampoline =FTRACE_GRAPH_TRAMP_ADDR , /* trampoline_size is only needed for dynamically allocated tramps */@@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {void fgraph_init_ops (struct ftrace_ops *dst_ops,struct ftrace_ops *src_ops) { - dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB ; + dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB | +FTRACE_OPS_FL_SAVE_ARGS ;#ifdef CONFIG_DYNAMIC_FTRACE if (src_ops) {@@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)#ifdef CONFIG_DYNAMIC_FTRACE static int fgraph_pid_func (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {struct trace_array *tr = gops->ops.private;int pid;@@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,return 0 ; } -return gops->saved_func (trace, gops); +return gops->saved_func (trace, gops, fregs); }void fgraph_update_pid_func (void )kernel/trace/ftrace.c +2
-1@@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable) }static int profile_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;kernel/trace/trace.h +2
-1@@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter);void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); -int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);void tracing_start_cmdline_record (void );void tracing_stop_cmdline_record (void );kernel/trace/trace_functions_graph.c +2
-1@@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void) }int trace_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;kernel/trace/trace_irqsoff.c +2
-1@@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set) }static int irqsoff_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set) }static int wakeup_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +5
-3@@ -773,7 +773,8 @@ struct fgraph_fixture { };static __init int store_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;@@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;/* Wrap the real function entry probe to avoid possible hanging */ static int trace_graph_entry_watchdog (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {/* This is harmlessly racy, we want to approximately detect a hang */ if (unlikely (++graph_hang_thresh >GRAPH_MAX_FUNC_TEST )) {@@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,return 0 ; } -return trace_graph_entry (trace, gops); +return trace_graph_entry (trace, gops, fregs); }static struct fgraph_ops fgraph_ops__initdata = {
Steven Rostedt
maintainerRe: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the RISC-V maintainers for this patch?
Thanks!
-- Steve
Quoted 476 lines · On Fri, 13 Sep 2024 00:08:40 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::entryfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v11:
- Update for the latest for-next branch.
Changes in v8:
- Just passftrace_regsto the handler instead of adding a new entryregfunc.
- Update riscvftrace_graph_func().
Changes in v3:
- Update for new multiple fgraph.--- arch/arm64/kernel/ftrace.c | 20 +++++++++++- arch/loongarch/kernel/ftrace_dyn.c | 10 +++++- arch/powerpc/kernel/trace/ftrace.c | 2 + arch/powerpc/kernel/trace/ftrace_64_pg.c | 10 ++++-- arch/riscv/kernel/ftrace.c | 17 ++++++++++ arch/x86/kernel/ftrace.c | 50 +++++++++++++++++++++--------- include/linux/ftrace.h | 18 ++++++++--- kernel/trace/fgraph.c | 23 ++++++++------ kernel/trace/ftrace.c | 3 +- kernel/trace/trace.h | 3 +- kernel/trace/trace_functions_graph.c | 3 +- kernel/trace/trace_irqsoff.c | 3 +- kernel/trace/trace_sched_wakeup.c | 3 +- kernel/trace/trace_selftest.c | 8 +++-- 14 files changed, 128 insertions(+), 45 deletions(-)arch/arm64/kernel/ftrace.c +19
-1@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (ip, &fregs->lr, fregs->fp); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->fp; +unsigned long *parent = &fregs->lr; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* Note: +* No protection against faulting at *parent, which may be seen +* on other archs. It's unlikely on AArch64. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, + (void *)frame_pointer, fregs)) { + *parent = return_hooker; + } }#else /* arch/loongarch/kernel/ftrace_dyn.c +9
-1@@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *parent = (unsigned long *)®s->regs[1 ]; +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + + old = *parent; -prepare_ftrace_return (ip, (unsigned long *)parent); +if (!function_graph_enter_regs (old, ip,0 , parent, fregs)) + *parent = return_hooker; }#else static int ftrace_modify_graph_caller (bool enable)arch/powerpc/kernel/trace/ftrace.c +1
-1@@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,if (bit <0 ) goto out; -if (!function_graph_enter (parent_ip, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent_ip, ip,0 , (unsigned long *)sp, fregs)) parent_ip =ppc_function_entry (return_to_handler);ftrace_test_recursion_unlock (bit);arch/powerpc/kernel/trace/ftrace_64_pg.c +6
-4@@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)* in current thread info. Return the address we want to divert to. */ static unsigned long -__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) +__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp, +struct ftrace_regs *fregs) {unsigned long return_hooker;int bit;@@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp return_hooker =ppc_function_entry (return_to_handler); -if (!function_graph_enter (parent, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent, ip,0 , (unsigned long *)sp, fregs)) parent = return_hooker;ftrace_test_recursion_unlock (bit);@@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long spvoid ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { - fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, fregs->regs.gpr[1 ]); + fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, + fregs->regs.gpr[1 ], fregs); }#else unsigned long prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) { -return __prepare_ftrace_return (parent, ip, sp); +return __prepare_ftrace_return (parent, ip, sp,NULL ); }#endif #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ arch/riscv/kernel/ftrace.c +16
-1@@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (&fregs->ra, ip, fregs->s0); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->s0; +unsigned long *parent = &fregs->ra; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* We don't suffer access faults, so no extra fault-recovery assembly +* is needed here. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, parent, fregs)) + *parent = return_hooker; }#else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */ extern void ftrace_graph_call (void );arch/x86/kernel/ftrace.c +35
-15@@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void) }#endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ -/* -* Hook the return address and push it in the stack of return addrs -* in current thread info. -*/ -void prepare_ftrace_return (unsigned long ip,unsigned long *parent, -unsigned long frame_pointer) +static inline bool skip_ftrace_return (void ) { -unsigned long return_hooker = (unsigned long )&return_to_handler; -int bit; -/* * When resuming from suspend-to-ram, this function can be indirectly * called from early CPU startup code while the CPU is in real mode, @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,* This check isn't as accurate as virt_addr_valid(), but it should be * good enough for this purpose, and it's fast. */ -if (unlikely ((long )__builtin_frame_address (0 ) >=0 )) -return ; +if ((long )__builtin_frame_address (0 ) >=0 ) +return true; -if (unlikely (ftrace_graph_is_dead ())) -return ; +if (ftrace_graph_is_dead ()) +return true; + +if (atomic_read (¤t->tracing_graph_pause)) +return true; +return false; +} + +/* +* Hook the return address and push it in the stack of return addrs +* in current thread info. +*/ +void prepare_ftrace_return (unsigned long ip,unsigned long *parent, +unsigned long frame_pointer) +{ +unsigned long return_hooker = (unsigned long )&return_to_handler; +int bit; -if (unlikely (atomic_read (¤t->tracing_graph_pause))) +if (unlikely (skip_ftrace_return ()))return ; bit =ftrace_test_recursion_trylock (ip, *parent);@@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *stack = (unsigned long *)kernel_stack_pointer (regs); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long *parent = (unsigned long *)stack; +int bit; -prepare_ftrace_return (ip, (unsigned long *)stack,0 ); +if (unlikely (skip_ftrace_return ())) +return ; + + bit =ftrace_test_recursion_trylock (ip, *parent); +if (bit <0 ) +return ; + +if (!function_graph_enter_regs (*parent, ip,0 , parent, fregs)) + *parent = return_hooker; + +ftrace_test_recursion_unlock (bit); }#endif include/linux/ftrace.h +14
-4@@ -1063,9 +1063,12 @@ struct fgraph_ops;typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *,struct fgraph_ops *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *, -struct fgraph_ops *);/* entry */ +struct fgraph_ops *, +struct ftrace_regs *);/* entry */ -extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, +struct fgraph_ops *gops, +struct ftrace_regs *fregs);bool ftrace_pids_enabled (struct ftrace_ops *ops);#ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {extern void return_to_handler (void );extern int -function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp); +function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs); + +static inline int function_graph_enter (unsigned long ret,unsigned long func, +unsigned long fp,unsigned long *retp) +{ +return function_graph_enter_regs (ret, func, fp, retp,NULL ); +}struct ftrace_ret_stack *ftrace_graph_get_ret_stack (struct task_struct *task,int skip);kernel/trace/fgraph.c +13
-10@@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset) }/* ftrace_graph_entry set to this to tell some archs to run function graph */ -static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops) +static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) {return 0 ; }@@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)#endif int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {return 0 ; }@@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,#endif /* If the caller does not use ftrace, call this function. */ -int function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp) +int function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs) {struct ftrace_graph_ent trace;unsigned long bitmap =0 ;@@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,if (static_branch_likely (&fgraph_do_direct)) {int save_curr_ret_stack = current->curr_ret_stack; -if (static_call (fgraph_func)(&trace, fgraph_direct_gops)) +if (static_call (fgraph_func)(&trace, fgraph_direct_gops, fregs)) bitmap |=BIT (fgraph_direct_gops->idx);else /* Clear out any saved storage */ @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func, save_curr_ret_stack = current->curr_ret_stack;if (ftrace_ops_test (&gops->ops, func,NULL ) && - gops->entryfunc (&trace, gops)) + gops->entryfunc (&trace, gops, fregs)) bitmap |=BIT (i);else /* Clear out any saved storage */ @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,static struct ftrace_ops graph_ops = { .func = ftrace_graph_func, - .flags =FTRACE_OPS_GRAPH_STUB , + .flags =FTRACE_OPS_GRAPH_STUB |FTRACE_OPS_FL_SAVE_ARGS , #ifdef FTRACE_GRAPH_TRAMP_ADDR .trampoline =FTRACE_GRAPH_TRAMP_ADDR , /* trampoline_size is only needed for dynamically allocated tramps */@@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {void fgraph_init_ops (struct ftrace_ops *dst_ops,struct ftrace_ops *src_ops) { - dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB ; + dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB | +FTRACE_OPS_FL_SAVE_ARGS ;#ifdef CONFIG_DYNAMIC_FTRACE if (src_ops) {@@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)#ifdef CONFIG_DYNAMIC_FTRACE static int fgraph_pid_func (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {struct trace_array *tr = gops->ops.private;int pid;@@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,return 0 ; } -return gops->saved_func (trace, gops); +return gops->saved_func (trace, gops, fregs); }void fgraph_update_pid_func (void )kernel/trace/ftrace.c +2
-1@@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable) }static int profile_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;kernel/trace/trace.h +2
-1@@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter);void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); -int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);void tracing_start_cmdline_record (void );void tracing_stop_cmdline_record (void );kernel/trace/trace_functions_graph.c +2
-1@@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void) }int trace_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;kernel/trace/trace_irqsoff.c +2
-1@@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set) }static int irqsoff_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set) }static int wakeup_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +5
-3@@ -773,7 +773,8 @@ struct fgraph_fixture { };static __init int store_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;@@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;/* Wrap the real function entry probe to avoid possible hanging */ static int trace_graph_entry_watchdog (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {/* This is harmlessly racy, we want to approximately detect a hang */ if (unlikely (++graph_hang_thresh >GRAPH_MAX_FUNC_TEST )) {@@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,return 0 ; } -return trace_graph_entry (trace, gops); +return trace_graph_entry (trace, gops, fregs); }static struct fgraph_ops fgraph_ops__initdata = {
Steven Rostedt
maintainerRe: [PATCH v14 03/19] function_graph: Pass ftrace_regs to entryfunc
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the X86 maintainers for this patch?
Thanks!
-- Steve
Quoted 476 lines · On Fri, 13 Sep 2024 00:08:40 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::entryfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v11:
- Update for the latest for-next branch.
Changes in v8:
- Just passftrace_regsto the handler instead of adding a new entryregfunc.
- Update riscvftrace_graph_func().
Changes in v3:
- Update for new multiple fgraph.--- arch/arm64/kernel/ftrace.c | 20 +++++++++++- arch/loongarch/kernel/ftrace_dyn.c | 10 +++++- arch/powerpc/kernel/trace/ftrace.c | 2 + arch/powerpc/kernel/trace/ftrace_64_pg.c | 10 ++++-- arch/riscv/kernel/ftrace.c | 17 ++++++++++ arch/x86/kernel/ftrace.c | 50 +++++++++++++++++++++--------- include/linux/ftrace.h | 18 ++++++++--- kernel/trace/fgraph.c | 23 ++++++++------ kernel/trace/ftrace.c | 3 +- kernel/trace/trace.h | 3 +- kernel/trace/trace_functions_graph.c | 3 +- kernel/trace/trace_irqsoff.c | 3 +- kernel/trace/trace_sched_wakeup.c | 3 +- kernel/trace/trace_selftest.c | 8 +++-- 14 files changed, 128 insertions(+), 45 deletions(-)arch/arm64/kernel/ftrace.c +19
-1@@ -481,7 +481,25 @@ void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (ip, &fregs->lr, fregs->fp); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->fp; +unsigned long *parent = &fregs->lr; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* Note: +* No protection against faulting at *parent, which may be seen +* on other archs. It's unlikely on AArch64. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, + (void *)frame_pointer, fregs)) { + *parent = return_hooker; + } }#else /* arch/loongarch/kernel/ftrace_dyn.c +9
-1@@ -243,8 +243,16 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *parent = (unsigned long *)®s->regs[1 ]; +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + + old = *parent; -prepare_ftrace_return (ip, (unsigned long *)parent); +if (!function_graph_enter_regs (old, ip,0 , parent, fregs)) + *parent = return_hooker; }#else static int ftrace_modify_graph_caller (bool enable)arch/powerpc/kernel/trace/ftrace.c +1
-1@@ -434,7 +434,7 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,if (bit <0 ) goto out; -if (!function_graph_enter (parent_ip, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent_ip, ip,0 , (unsigned long *)sp, fregs)) parent_ip =ppc_function_entry (return_to_handler);ftrace_test_recursion_unlock (bit);arch/powerpc/kernel/trace/ftrace_64_pg.c +6
-4@@ -800,7 +800,8 @@ int ftrace_disable_ftrace_graph_caller(void)* in current thread info. Return the address we want to divert to. */ static unsigned long -__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) +__prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp, +struct ftrace_regs *fregs) {unsigned long return_hooker;int bit;@@ -817,7 +818,7 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long sp return_hooker =ppc_function_entry (return_to_handler); -if (!function_graph_enter (parent, ip,0 , (unsigned long *)sp)) +if (!function_graph_enter_regs (parent, ip,0 , (unsigned long *)sp, fregs)) parent = return_hooker;ftrace_test_recursion_unlock (bit);@@ -829,13 +830,14 @@ __prepare_ftrace_return(unsigned long parent, unsigned long ip, unsigned long spvoid ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { - fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, fregs->regs.gpr[1 ]); + fregs->regs.link =__prepare_ftrace_return (parent_ip, ip, + fregs->regs.gpr[1 ], fregs); }#else unsigned long prepare_ftrace_return (unsigned long parent,unsigned long ip,unsigned long sp) { -return __prepare_ftrace_return (parent, ip, sp); +return __prepare_ftrace_return (parent, ip, sp,NULL ); }#endif #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ arch/riscv/kernel/ftrace.c +16
-1@@ -214,7 +214,22 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,void ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs) { -prepare_ftrace_return (&fregs->ra, ip, fregs->s0); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long frame_pointer = fregs->s0; +unsigned long *parent = &fregs->ra; +unsigned long old; + +if (unlikely (atomic_read (¤t->tracing_graph_pause))) +return ; + +/* +* We don't suffer access faults, so no extra fault-recovery assembly +* is needed here. +*/ + old = *parent; + +if (!function_graph_enter_regs (old, ip, frame_pointer, parent, fregs)) + *parent = return_hooker; }#else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */ extern void ftrace_graph_call (void );arch/x86/kernel/ftrace.c +35
-15@@ -605,16 +605,8 @@ int ftrace_disable_ftrace_graph_caller(void) }#endif /* CONFIG_DYNAMIC_FTRACE && !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ -/* -* Hook the return address and push it in the stack of return addrs -* in current thread info. -*/ -void prepare_ftrace_return (unsigned long ip,unsigned long *parent, -unsigned long frame_pointer) +static inline bool skip_ftrace_return (void ) { -unsigned long return_hooker = (unsigned long )&return_to_handler; -int bit; -/* * When resuming from suspend-to-ram, this function can be indirectly * called from early CPU startup code while the CPU is in real mode, @@ -624,13 +616,28 @@ void prepare_ftrace_return(unsigned long ip, unsigned long *parent,* This check isn't as accurate as virt_addr_valid(), but it should be * good enough for this purpose, and it's fast. */ -if (unlikely ((long )__builtin_frame_address (0 ) >=0 )) -return ; +if ((long )__builtin_frame_address (0 ) >=0 ) +return true; -if (unlikely (ftrace_graph_is_dead ())) -return ; +if (ftrace_graph_is_dead ()) +return true; + +if (atomic_read (¤t->tracing_graph_pause)) +return true; +return false; +} + +/* +* Hook the return address and push it in the stack of return addrs +* in current thread info. +*/ +void prepare_ftrace_return (unsigned long ip,unsigned long *parent, +unsigned long frame_pointer) +{ +unsigned long return_hooker = (unsigned long )&return_to_handler; +int bit; -if (unlikely (atomic_read (¤t->tracing_graph_pause))) +if (unlikely (skip_ftrace_return ()))return ; bit =ftrace_test_recursion_trylock (ip, *parent);@@ -649,8 +656,21 @@ void ftrace_graph_func(unsigned long ip, unsigned long parent_ip, {struct pt_regs *regs = &fregs->regs;unsigned long *stack = (unsigned long *)kernel_stack_pointer (regs); +unsigned long return_hooker = (unsigned long )&return_to_handler; +unsigned long *parent = (unsigned long *)stack; +int bit; -prepare_ftrace_return (ip, (unsigned long *)stack,0 ); +if (unlikely (skip_ftrace_return ())) +return ; + + bit =ftrace_test_recursion_trylock (ip, *parent); +if (bit <0 ) +return ; + +if (!function_graph_enter_regs (*parent, ip,0 , parent, fregs)) + *parent = return_hooker; + +ftrace_test_recursion_unlock (bit); }#endif include/linux/ftrace.h +14
-4@@ -1063,9 +1063,12 @@ struct fgraph_ops;typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *,struct fgraph_ops *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *, -struct fgraph_ops *);/* entry */ +struct fgraph_ops *, +struct ftrace_regs *);/* entry */ -extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +extern int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, +struct fgraph_ops *gops, +struct ftrace_regs *fregs);bool ftrace_pids_enabled (struct ftrace_ops *ops);#ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -1108,8 +1111,15 @@ struct ftrace_ret_stack {extern void return_to_handler (void );extern int -function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp); +function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs); + +static inline int function_graph_enter (unsigned long ret,unsigned long func, +unsigned long fp,unsigned long *retp) +{ +return function_graph_enter_regs (ret, func, fp, retp,NULL ); +}struct ftrace_ret_stack *ftrace_graph_get_ret_stack (struct task_struct *task,int skip);kernel/trace/fgraph.c +13
-10@@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset) }/* ftrace_graph_entry set to this to tell some archs to run function graph */ -static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops) +static int entry_run (struct ftrace_graph_ent *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) {return 0 ; }@@ -484,7 +485,7 @@ int __weak ftrace_disable_ftrace_graph_caller(void)#endif int ftrace_graph_entry_stub (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {return 0 ; }@@ -612,8 +613,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,#endif /* If the caller does not use ftrace, call this function. */ -int function_graph_enter (unsigned long ret,unsigned long func, -unsigned long frame_pointer,unsigned long *retp) +int function_graph_enter_regs (unsigned long ret,unsigned long func, +unsigned long frame_pointer,unsigned long *retp, +struct ftrace_regs *fregs) {struct ftrace_graph_ent trace;unsigned long bitmap =0 ;@@ -631,7 +633,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,if (static_branch_likely (&fgraph_do_direct)) {int save_curr_ret_stack = current->curr_ret_stack; -if (static_call (fgraph_func)(&trace, fgraph_direct_gops)) +if (static_call (fgraph_func)(&trace, fgraph_direct_gops, fregs)) bitmap |=BIT (fgraph_direct_gops->idx);else /* Clear out any saved storage */ @@ -649,7 +651,7 @@ int function_graph_enter(unsigned long ret, unsigned long func, save_curr_ret_stack = current->curr_ret_stack;if (ftrace_ops_test (&gops->ops, func,NULL ) && - gops->entryfunc (&trace, gops)) + gops->entryfunc (&trace, gops, fregs)) bitmap |=BIT (i);else /* Clear out any saved storage */ @@ -925,7 +927,7 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,static struct ftrace_ops graph_ops = { .func = ftrace_graph_func, - .flags =FTRACE_OPS_GRAPH_STUB , + .flags =FTRACE_OPS_GRAPH_STUB |FTRACE_OPS_FL_SAVE_ARGS , #ifdef FTRACE_GRAPH_TRAMP_ADDR .trampoline =FTRACE_GRAPH_TRAMP_ADDR , /* trampoline_size is only needed for dynamically allocated tramps */@@ -935,7 +937,8 @@ static struct ftrace_ops graph_ops = {void fgraph_init_ops (struct ftrace_ops *dst_ops,struct ftrace_ops *src_ops) { - dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB ; + dst_ops->flags =FTRACE_OPS_FL_PID |FTRACE_OPS_GRAPH_STUB | +FTRACE_OPS_FL_SAVE_ARGS ;#ifdef CONFIG_DYNAMIC_FTRACE if (src_ops) {@@ -1119,7 +1122,7 @@ void ftrace_graph_exit_task(struct task_struct *t)#ifdef CONFIG_DYNAMIC_FTRACE static int fgraph_pid_func (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {struct trace_array *tr = gops->ops.private;int pid;@@ -1133,7 +1136,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,return 0 ; } -return gops->saved_func (trace, gops); +return gops->saved_func (trace, gops, fregs); }void fgraph_update_pid_func (void )kernel/trace/ftrace.c +2
-1@@ -821,7 +821,8 @@ void ftrace_graph_graph_time_control(bool enable) }static int profile_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;kernel/trace/trace.h +2
-1@@ -683,7 +683,8 @@ void trace_default_header(struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter);void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); -int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops); +int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);void tracing_start_cmdline_record (void );void tracing_stop_cmdline_record (void );kernel/trace/trace_functions_graph.c +2
-1@@ -128,7 +128,8 @@ static inline int ftrace_graph_ignore_irqs(void) }int trace_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;kernel/trace/trace_irqsoff.c +2
-1@@ -176,7 +176,8 @@ static int irqsoff_display_graph(struct trace_array *tr, int set) }static int irqsoff_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -113,7 +113,8 @@ static int wakeup_display_graph(struct trace_array *tr, int set) }static int wakeup_graph_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +5
-3@@ -773,7 +773,8 @@ struct fgraph_fixture { };static __init int store_entry (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;@@ -1024,7 +1025,8 @@ static unsigned int graph_hang_thresh;/* Wrap the real function entry probe to avoid possible hanging */ static int trace_graph_entry_watchdog (struct ftrace_graph_ent *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {/* This is harmlessly racy, we want to approximately detect a hang */ if (unlikely (++graph_hang_thresh >GRAPH_MAX_FUNC_TEST )) {@@ -1038,7 +1040,7 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace,return 0 ; } -return trace_graph_entry (trace, gops); +return trace_graph_entry (trace, gops, fregs); }static struct fgraph_ops fgraph_ops__initdata = {
Masami Hiramatsu (Google)
maintainerPATCH v14 4/19 function_graph: Replace fgraph_ret_regs with ftrace_regs
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Use ftrace_regs instead of fgraph_ret_regs for tracing return value on function_graph tracer because of simplifying the callback interface.
The CONFIG_HAVE_FUNCTION_GRAPH_RETVAL is also replaced by CONFIG_HAVE_FUNCTION_GRAPH_FREGS.
---
Changes in v8:
- Newly added.
--- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 23 ++++++----------------- arch/arm64/kernel/asm-offsets.c | 12 ------------ arch/arm64/kernel/entry-ftrace.S | 32 ++++++++++++++++++-------------- arch/loongarch/Kconfig | 2 +- arch/loongarch/include/asm/ftrace.h | 24 ++---------------------- arch/loongarch/kernel/asm-offsets.c | 12 ------------ arch/loongarch/kernel/mcount.S | 17 ++++++++++------- arch/loongarch/kernel/mcount_dyn.S | 14 +++++++------- arch/riscv/Kconfig | 2 +- arch/riscv/include/asm/ftrace.h | 26 +++++--------------------- arch/riscv/kernel/mcount.S | 24 +++++++++++++----------- arch/s390/Kconfig | 2 +- arch/s390/include/asm/ftrace.h | 26 +++++++++----------------- arch/s390/kernel/asm-offsets.c | 6 ------ arch/s390/kernel/mcount.S | 9 +++++---- arch/x86/Kconfig | 2 +- arch/x86/include/asm/ftrace.h | 22 ++-------------------- arch/x86/kernel/ftrace_32.S | 15 +++++++++------ arch/x86/kernel/ftrace_64.S | 17 +++++++++-------- include/linux/ftrace.h | 14 +++++++++++--- kernel/trace/Kconfig | 4 ++-- kernel/trace/fgraph.c | 21 +++++++++------------ 23 files changed, 122 insertions(+), 205 deletions(-)
arch/arm64/Kconfig +1 -0
@@ -211,6 +211,7 @@ config ARM64 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_ERROR_INJECTION + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_RETVAL select HAVE_GCC_PLUGINS
arch/arm64/include/asm/ftrace.h +6 -17
@@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs) fregs->pc = fregs->lr; } +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->fp; +} +int ftrace_regs_query_register_offset (const char *name);int ftrace_init_nop (struct module *mod,struct dyn_ftrace *rec);
@@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,#ifndef __ASSEMBLY__#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* x0 - x7 */ -unsigned long regs[8 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -}void prepare_ftrace_return (unsigned long self_addr,unsigned long *parent,unsigned long frame_pointer);
arch/arm64/kernel/asm-offsets.c +0 -12
@@ -201,18 +201,6 @@ int main(void)DEFINE (FTRACE_OPS_FUNC , offsetof(struct ftrace_ops , func));#endif BLANK (); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - DEFINE(FGRET_REGS_X0, offsetof(struct fgraph_ret_regs, regs[0])); - DEFINE(FGRET_REGS_X1, offsetof(struct fgraph_ret_regs, regs[1])); - DEFINE(FGRET_REGS_X2, offsetof(struct fgraph_ret_regs, regs[2])); - DEFINE(FGRET_REGS_X3, offsetof(struct fgraph_ret_regs, regs[3])); - DEFINE(FGRET_REGS_X4, offsetof(struct fgraph_ret_regs, regs[4])); - DEFINE(FGRET_REGS_X5, offsetof(struct fgraph_ret_regs, regs[5])); - DEFINE(FGRET_REGS_X6, offsetof(struct fgraph_ret_regs, regs[6])); - DEFINE(FGRET_REGS_X7, offsetof(struct fgraph_ret_regs, regs[7])); -DEFINE (FGRET_REGS_FP , offsetof(struct fgraph_ret_regs , fp)); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -#endif #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS DEFINE (FTRACE_OPS_DIRECT_CALL , offsetof(struct ftrace_ops , direct_call));#endif
arch/arm64/kernel/entry-ftrace.S +18 -14
@@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph) * @fp is checked against the value passed by ftrace_graph_caller(). */ SYM_CODE_START(return_to_handler) - /* save return value regs */ - sub sp, sp, #FGRET_REGS_SIZE - stp x0, x1, [sp, #FGRET_REGS_X0] - stp x2, x3, [sp, #FGRET_REGS_X2] - stp x4, x5, [sp, #FGRET_REGS_X4] - stp x6, x7, [sp, #FGRET_REGS_X6] - str x29, [sp, #FGRET_REGS_FP] // parent's fp + /* Make room for ftrace_regs */ + sub sp, sp, #FREGS_SIZE + + /* Save return value regs */ + stp x0, x1, [sp, #FREGS_X0] + stp x2, x3, [sp, #FREGS_X2] + stp x4, x5, [sp, #FREGS_X4] + stp x6, x7, [sp, #FREGS_X6] + + /* Save the callsite's FP */ + str x29, [sp, #FREGS_FP] mov x0, sp - bl ftrace_return_to_handler // addr = ftrace_return_to_hander(regs); + bl ftrace_return_to_handler // addr = ftrace_return_to_hander(fregs); mov x30, x0 // restore the original return address - /* restore return value regs */ - ldp x0, x1, [sp, #FGRET_REGS_X0] - ldp x2, x3, [sp, #FGRET_REGS_X2] - ldp x4, x5, [sp, #FGRET_REGS_X4] - ldp x6, x7, [sp, #FGRET_REGS_X6] - add sp, sp, #FGRET_REGS_SIZE + /* Restore return value regs */ + ldp x0, x1, [sp, #FREGS_X0] + ldp x2, x3, [sp, #FREGS_X2] + ldp x4, x5, [sp, #FREGS_X4] + ldp x6, x7, [sp, #FREGS_X6] + add sp, sp, #FREGS_SIZE ret SYM_CODE_END(return_to_handler)
arch/loongarch/Kconfig +1 -1
@@ -131,7 +131,7 @@ config LOONGARCH select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINS
arch/loongarch/include/asm/ftrace.h +2 -22
@@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip) 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) \ + ((fregs)->regs.regs[22])#define ftrace_graph_func ftrace_graph_func void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
@@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)#endif /* CONFIG_FUNCTION_TRACER */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* a0 - a1 */ -unsigned long regs[2 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_LOONGARCH_FTRACE_H */
arch/loongarch/kernel/asm-offsets.c +0 -12
@@ -279,18 +279,6 @@ static void __used output_pbe_defines(void) }#endif -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static void __used output_fgraph_ret_regs_defines (void ) -{ -COMMENT ("LoongArch fgraph_ret_regs offsets." ); -OFFSET (FGRET_REGS_A0 , fgraph_ret_regs, regs[0 ]); -OFFSET (FGRET_REGS_A1 , fgraph_ret_regs, regs[1 ]); -OFFSET (FGRET_REGS_FP , fgraph_ret_regs, fp); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -BLANK (); -} -#endif -static void __used output_kvm_defines (void ) {COMMENT ("KVM/LoongArch Specific offsets." );
arch/loongarch/kernel/mcount.S +10 -7
@@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller) SYM_FUNC_END(ftrace_graph_caller) SYM_FUNC_START(return_to_handler) - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + /* Save return value regs */ + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler
@@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler) /* Restore the real parent address: a0 -> ra */ move ra, a0 - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + /* Restore return value regs */ + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE + jr ra SYM_FUNC_END(return_to_handler) #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
arch/loongarch/kernel/mcount_dyn.S +7 -7
@@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED /* Save return value regs */ - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler move ra, a0 /* Restore return value regs */ - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE jr ra SYM_CODE_END(return_to_handler)
arch/riscv/Kconfig +1 -1
@@ -140,7 +140,7 @@ config RISCV select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_GRAPH_TRACER - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION select HAVE_EBPF_JIT if MMU select HAVE_GUP_FAST if MMU
arch/riscv/include/asm/ftrace.h +5 -21
@@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const structreturn fregs->sp; } +static __always_inline unsigned long ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->s0; +} +static __always_inline unsigned long ftrace_regs_get_argument (struct ftrace_regs *fregs,unsigned int n) {
@@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi#endif /* CONFIG_DYNAMIC_FTRACE */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long a1; -unsigned long a0; -unsigned long s0; -unsigned long ra; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->a0; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->s0; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_RISCV_FTRACE_H */
arch/riscv/kernel/mcount.S +13 -11
@@ -12,6 +12,8 @@ #include <asm/asm-offsets.h> #include <asm/ftrace.h> +#define ABI_SIZE_ON_STACK 80 + .text .macro SAVE_ABI_STATE
@@ -26,12 +28,12 @@ * register if a0 was not saved. */ .macro SAVE_RET_ABI_STATE - addi sp, sp, -4*SZREG - REG_S s0, 2*SZREG(sp) - REG_S ra, 3*SZREG(sp) - REG_S a0, 1*SZREG(sp) - REG_S a1, 0*SZREG(sp) - addi s0, sp, 4*SZREG + addi sp, sp, -ABI_SIZE_ON_STACK + REG_S ra, 1*SZREG(sp) + REG_S s0, 8*SZREG(sp) + REG_S a0, 10*SZREG(sp) + REG_S a1, 11*SZREG(sp) + addi s0, sp, ABI_SIZE_ON_STACK .endm .macro RESTORE_ABI_STATE
@@ -41,11 +43,11 @@ .endm .macro RESTORE_RET_ABI_STATE - REG_L ra, 3*SZREG(sp) - REG_L s0, 2*SZREG(sp) - REG_L a0, 1*SZREG(sp) - REG_L a1, 0*SZREG(sp) - addi sp, sp, 4*SZREG + REG_L ra, 1*SZREG(sp) + REG_L s0, 8*SZREG(sp) + REG_L a0, 10*SZREG(sp) + REG_L a1, 11*SZREG(sp) + addi sp, sp, ABI_SIZE_ON_STACK .endm SYM_TYPED_FUNC_START(ftrace_stub)
arch/s390/Kconfig +1 -1
@@ -184,7 +184,7 @@ config S390 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINS
arch/s390/include/asm/ftrace.h +9 -17
@@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *return NULL ; } -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long gpr2; -unsigned long fp; -}; - -static __always_inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->gpr2; -} - -static __always_inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ -static __always_inline unsigned long ftrace_regs_get_instruction_pointer (const struct ftrace_regs *fregs) {
@@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (struct ftrace_regs *fregs) +{ +unsigned long *sp; + + sp = (void *)ftrace_regs_get_stack_pointer (fregs); +return sp[0 ];/* return backchain */ +} +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS /* * When an ftrace registered caller is tracing a function that is
arch/s390/kernel/asm-offsets.c +0 -6
@@ -179,12 +179,6 @@ int main(void)DEFINE (OLDMEM_SIZE ,PARMAREA + offsetof(struct parmarea , oldmem_size));DEFINE (COMMAND_LINE ,PARMAREA + offsetof(struct parmarea , command_line));DEFINE (MAX_COMMAND_LINE_SIZE ,PARMAREA + offsetof(struct parmarea , max_command_line_size)); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -/* function graph return value tracing */ -OFFSET (__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2); -OFFSET (__FGRAPH_RET_FP, fgraph_ret_regs, fp); -DEFINE (__FGRAPH_RET_SIZE,sizeof (struct fgraph_ret_regs )); -#endif OFFSET (__FTRACE_REGS_PT_REGS, ftrace_regs, regs);DEFINE (__FTRACE_REGS_SIZE,sizeof (struct ftrace_regs ));
arch/s390/kernel/mcount.S +5 -4
@@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common) SYM_FUNC_START(return_to_handler) stmg %r2,%r5,32(%r15) lgr %r1,%r15 - aghi %r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE) +# Allocate ftrace_regs + backchain on the stack + aghi %r15,-STACK_FRAME_SIZE_FREGS stg %r1,__SF_BACKCHAIN(%r15) la %r3,STACK_FRAME_OVERHEAD(%r15) - stg %r1,__FGRAPH_RET_FP(%r3) - stg %r2,__FGRAPH_RET_GPR2(%r3) + stg %r2,(__SF_GPRS+2*8)(%r15) + stg %r15,(__SF_GPRS+15*8)(%r15) lgr %r2,%r3 brasl %r14,ftrace_return_to_handler - aghi %r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE + aghi %r15,STACK_FRAME_SIZE_FREGS lgr %r14,%r2 lmg %r2,%r5,32(%r15) BR_EX %r14
arch/x86/Kconfig +1 -1
@@ -228,7 +228,7 @@ config X86 select HAVE_GUP_FAST select HAVE_FENTRY if X86_64 || DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER if X86_32 || (X86_64 && DYNAMIC_FTRACE) select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINS
arch/x86/include/asm/ftrace.h +2 -20
@@ -64,6 +64,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
@@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)#endif /* !COMPILE_OFFSETS */ #endif /* !__ASSEMBLY__ */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long ax; -unsigned long dx; -unsigned long bp; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->ax; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->bp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_X86_FTRACE_H */
arch/x86/kernel/ftrace_32.S +9 -6
@@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__) SYM_FUNC_END(__fentry__) EXPORT_SYMBOL(__fentry__) +#define FRAME_SIZE PT_OLDSS+4 + SYM_CODE_START(ftrace_caller) #ifdef CONFIG_FRAME_POINTER
@@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller) .globl return_to_handler return_to_handler: - pushl $0 - pushl %edx - pushl %eax + subl $(FRAME_SIZE), %esp + movl $0, PT_EBP(%esp) + movl %edx, PT_EDX(%esp) + movl %eax, PT_EAX(%esp) movl %esp, %eax call ftrace_return_to_handler movl %eax, %ecx - popl %eax - popl %edx - addl $4, %esp # skip ebp + movl %eax, PT_EAX(%esp) + movl %edx, PT_EDX(%esp) + addl $(FRAME_SIZE), %esp JMP_NOSPEC ecx #endif
arch/x86/kernel/ftrace_64.S +9 -8
@@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED ANNOTATE_NOENDBR - subq $24, %rsp - /* Save the return values */ - movq %rax, (%rsp) - movq %rdx, 8(%rsp) - movq %rbp, 16(%rsp) + /* Save ftrace_regs for function exit context */ + subq $(FRAME_SIZE), %rsp + + movq %rax, RAX(%rsp) + movq %rdx, RDX(%rsp) + movq %rbp, RBP(%rsp) movq %rsp, %rdi call ftrace_return_to_handler movq %rax, %rdi - movq 8(%rsp), %rdx - movq (%rsp), %rax + movq RDX(%rsp), %rdx + movq RAX(%rsp), %rax - addq $24, %rsp + addq $(FRAME_SIZE), %rsp /* * Jump back to the old return address. This cannot be JMP_NOSPEC rdi * since IBT would demand that contain ENDBR, which simply isn't so for
include/linux/ftrace.h +11 -3
@@ -43,9 +43,8 @@ struct dyn_ftrace;char *arch_ftrace_match_adjust (char *str,const char *search); -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -struct fgraph_ret_regs ; -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs); +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs);#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer);#endif
@@ -134,6 +133,13 @@ extern int ftrace_enabled;* Also, architecture dependent fields can be used for internal process. * (e.g. orig_ax on x86_64) * +* Basically, ftrace_regs stores the registers related to the context. +* On function entry, registers for function parameters and hooking the +* function call are stored, and on function exit, registers for function +* return value and frame pointers are stored. +* +* And also, it dpends on the context that which registers are restored +* from the ftrace_regs. * On the function entry, those registers will be restored except for * the stack pointer, so that user can change the function parameters * and instruction pointer (e.g. live patching.)
@@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs) override_function_with_return(ftrace_get_regs(fregs))#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +#define ftrace_regs_get_frame_pointer (fregs) \ + frame_pointer(&(fregs)->regs)#endif typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
kernel/trace/Kconfig +2 -2
@@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER help See Documentation/trace/ftrace-design.rst -config HAVE_FUNCTION_GRAPH_RETVAL +config HAVE_FUNCTION_GRAPH_FREGS bool config HAVE_DYNAMIC_FTRACE
@@ -232,7 +232,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_FREGS depends on FUNCTION_GRAPH_TRACER default n help
kernel/trace/fgraph.c +9 -12
@@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = { .notifier_call = ftrace_suspend_notifier_call, }; -/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */ -struct fgraph_ret_regs ; -/* * 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, -unsigned long frame_pointer) +static inline unsigned long +__ftrace_return_to_handler (struct ftrace_regs *fregs,unsigned long frame_pointer) {struct ftrace_ret_stack *ret_stack;struct ftrace_graph_ret trace;
@@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs trace.rettime =trace_clock_local ();#ifdef CONFIG_FUNCTION_GRAPH_RETVAL - trace.retval =fgraph_ret_regs_return_value (ret_regs); + trace.retval =ftrace_regs_get_return_value (fregs);#endif bitmap =get_bitmap_bits (current, offset);
@@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs }/* -* After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can -* leave only ftrace_return_to_handler(ret_regs). +* After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can +* leave only ftrace_return_to_handler(fregs). */ -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs) +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs) { -return __ftrace_return_to_handler (ret_regs, -fgraph_ret_regs_frame_pointer (ret_regs)); +return __ftrace_return_to_handler (fregs, +ftrace_regs_get_frame_pointer (fregs)); }#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer)
Steven Rostedt
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Can I get an Acked-by from the AARCH64 maintainers for this patch?
Thanks!
-- Steve
Quoted 739 lines · On Fri, 13 Sep 2024 00:08:51 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Use
ftrace_regsinstead offgraph_ret_regsfor tracing return value onfunction_graphtracer because of simplifying the callback interface.The
CONFIG_HAVE_FUNCTION_GRAPH_RETVALis also replaced byCONFIG_HAVE_FUNCTION_GRAPH_FREGS.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Newly added.--- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 23 ++++++----------------- arch/arm64/kernel/asm-offsets.c | 12 ------------ arch/arm64/kernel/entry-ftrace.S | 32 ++++++++++++++++++-------------- arch/loongarch/Kconfig | 2 +- arch/loongarch/include/asm/ftrace.h | 24 ++---------------------- arch/loongarch/kernel/asm-offsets.c | 12 ------------ arch/loongarch/kernel/mcount.S | 17 ++++++++++------- arch/loongarch/kernel/mcount_dyn.S | 14 +++++++------- arch/riscv/Kconfig | 2 +- arch/riscv/include/asm/ftrace.h | 26 +++++--------------------- arch/riscv/kernel/mcount.S | 24 +++++++++++++----------- arch/s390/Kconfig | 2 +- arch/s390/include/asm/ftrace.h | 26 +++++++++----------------- arch/s390/kernel/asm-offsets.c | 6 ------ arch/s390/kernel/mcount.S | 9 +++++---- arch/x86/Kconfig | 2 +- arch/x86/include/asm/ftrace.h | 22 ++-------------------- arch/x86/kernel/ftrace_32.S | 15 +++++++++------ arch/x86/kernel/ftrace_64.S | 17 +++++++++-------- include/linux/ftrace.h | 14 +++++++++++--- kernel/trace/Kconfig | 4 ++-- kernel/trace/fgraph.c | 21 +++++++++------------ 23 files changed, 122 insertions(+), 205 deletions(-)arch/arm64/Kconfig +1
-0@@ -211,6 +211,7 @@ config ARM64 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_ERROR_INJECTION + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_RETVAL select HAVE_GCC_PLUGINSarch/arm64/include/asm/ftrace.h +6
-17@@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs) fregs->pc = fregs->lr; } +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->fp; +} +int ftrace_regs_query_register_offset (const char *name);int ftrace_init_nop (struct module *mod,struct dyn_ftrace *rec);@@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,#ifndef __ASSEMBLY__#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* x0 - x7 */ -unsigned long regs[8 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -}void prepare_ftrace_return (unsigned long self_addr,unsigned long *parent,unsigned long frame_pointer);arch/arm64/kernel/asm-offsets.c +0
-12@@ -201,18 +201,6 @@ int main(void)DEFINE (FTRACE_OPS_FUNC , offsetof(struct ftrace_ops , func));#endif BLANK (); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - DEFINE(FGRET_REGS_X0, offsetof(struct fgraph_ret_regs, regs[0])); - DEFINE(FGRET_REGS_X1, offsetof(struct fgraph_ret_regs, regs[1])); - DEFINE(FGRET_REGS_X2, offsetof(struct fgraph_ret_regs, regs[2])); - DEFINE(FGRET_REGS_X3, offsetof(struct fgraph_ret_regs, regs[3])); - DEFINE(FGRET_REGS_X4, offsetof(struct fgraph_ret_regs, regs[4])); - DEFINE(FGRET_REGS_X5, offsetof(struct fgraph_ret_regs, regs[5])); - DEFINE(FGRET_REGS_X6, offsetof(struct fgraph_ret_regs, regs[6])); - DEFINE(FGRET_REGS_X7, offsetof(struct fgraph_ret_regs, regs[7])); -DEFINE (FGRET_REGS_FP , offsetof(struct fgraph_ret_regs , fp)); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -#endif #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS DEFINE (FTRACE_OPS_DIRECT_CALL , offsetof(struct ftrace_ops , direct_call));#endif arch/arm64/kernel/entry-ftrace.S +18
-14@@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph) * @fp is checked against the value passed by ftrace_graph_caller(). */ SYM_CODE_START(return_to_handler) - /* save return value regs */ - sub sp, sp, #FGRET_REGS_SIZE - stp x0, x1, [sp, #FGRET_REGS_X0] - stp x2, x3, [sp, #FGRET_REGS_X2] - stp x4, x5, [sp, #FGRET_REGS_X4] - stp x6, x7, [sp, #FGRET_REGS_X6] - str x29, [sp, #FGRET_REGS_FP] // parent's fp + /* Make room for ftrace_regs */ + sub sp, sp, #FREGS_SIZE + + /* Save return value regs */ + stp x0, x1, [sp, #FREGS_X0] + stp x2, x3, [sp, #FREGS_X2] + stp x4, x5, [sp, #FREGS_X4] + stp x6, x7, [sp, #FREGS_X6] + + /* Save the callsite's FP */ + str x29, [sp, #FREGS_FP] mov x0, sp - bl ftrace_return_to_handler // addr = ftrace_return_to_hander(regs); + bl ftrace_return_to_handler // addr = ftrace_return_to_hander(fregs); mov x30, x0 // restore the original return address - /* restore return value regs */ - ldp x0, x1, [sp, #FGRET_REGS_X0] - ldp x2, x3, [sp, #FGRET_REGS_X2] - ldp x4, x5, [sp, #FGRET_REGS_X4] - ldp x6, x7, [sp, #FGRET_REGS_X6] - add sp, sp, #FGRET_REGS_SIZE + /* Restore return value regs */ + ldp x0, x1, [sp, #FREGS_X0] + ldp x2, x3, [sp, #FREGS_X2] + ldp x4, x5, [sp, #FREGS_X4] + ldp x6, x7, [sp, #FREGS_X6] + add sp, sp, #FREGS_SIZE ret SYM_CODE_END(return_to_handler)arch/loongarch/Kconfig +1
-1@@ -131,7 +131,7 @@ config LOONGARCH select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/loongarch/include/asm/ftrace.h +2
-22@@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip) 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) \ + ((fregs)->regs.regs[22])#define ftrace_graph_func ftrace_graph_func void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,@@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)#endif /* CONFIG_FUNCTION_TRACER */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* a0 - a1 */ -unsigned long regs[2 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_LOONGARCH_FTRACE_H */ arch/loongarch/kernel/asm-offsets.c +0
-12@@ -279,18 +279,6 @@ static void __used output_pbe_defines(void) }#endif -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static void __used output_fgraph_ret_regs_defines (void ) -{ -COMMENT ("LoongArch fgraph_ret_regs offsets." ); -OFFSET (FGRET_REGS_A0 , fgraph_ret_regs, regs[0 ]); -OFFSET (FGRET_REGS_A1 , fgraph_ret_regs, regs[1 ]); -OFFSET (FGRET_REGS_FP , fgraph_ret_regs, fp); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -BLANK (); -} -#endif -static void __used output_kvm_defines (void ) {COMMENT ("KVM/LoongArch Specific offsets." );arch/loongarch/kernel/mcount.S +10
-7@@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller) SYM_FUNC_END(ftrace_graph_caller) SYM_FUNC_START(return_to_handler) - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + /* Save return value regs */ + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler@@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler) /* Restore the real parent address: a0 -> ra */ move ra, a0 - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + /* Restore return value regs */ + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE + jr ra SYM_FUNC_END(return_to_handler) #endif /* CONFIG_FUNCTION_GRAPH_TRACER */arch/loongarch/kernel/mcount_dyn.S +7
-7@@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED /* Save return value regs */ - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler move ra, a0 /* Restore return value regs */ - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE jr ra SYM_CODE_END(return_to_handler)arch/riscv/Kconfig +1
-1@@ -140,7 +140,7 @@ config RISCV select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_GRAPH_TRACER - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION select HAVE_EBPF_JIT if MMU select HAVE_GUP_FAST if MMUarch/riscv/include/asm/ftrace.h +5
-21@@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const structreturn fregs->sp; } +static __always_inline unsigned long ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->s0; +} +static __always_inline unsigned long ftrace_regs_get_argument (struct ftrace_regs *fregs,unsigned int n) {@@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi#endif /* CONFIG_DYNAMIC_FTRACE */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long a1; -unsigned long a0; -unsigned long s0; -unsigned long ra; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->a0; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->s0; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_RISCV_FTRACE_H */ arch/riscv/kernel/mcount.S +13
-11@@ -12,6 +12,8 @@ #include <asm/asm-offsets.h> #include <asm/ftrace.h> +#define ABI_SIZE_ON_STACK 80 + .text .macro SAVE_ABI_STATE@@ -26,12 +28,12 @@ * register if a0 was not saved. */ .macro SAVE_RET_ABI_STATE - addi sp, sp, -4*SZREG - REG_S s0, 2*SZREG(sp) - REG_S ra, 3*SZREG(sp) - REG_S a0, 1*SZREG(sp) - REG_S a1, 0*SZREG(sp) - addi s0, sp, 4*SZREG + addi sp, sp, -ABI_SIZE_ON_STACK + REG_S ra, 1*SZREG(sp) + REG_S s0, 8*SZREG(sp) + REG_S a0, 10*SZREG(sp) + REG_S a1, 11*SZREG(sp) + addi s0, sp, ABI_SIZE_ON_STACK .endm .macro RESTORE_ABI_STATE@@ -41,11 +43,11 @@ .endm .macro RESTORE_RET_ABI_STATE - REG_L ra, 3*SZREG(sp) - REG_L s0, 2*SZREG(sp) - REG_L a0, 1*SZREG(sp) - REG_L a1, 0*SZREG(sp) - addi sp, sp, 4*SZREG + REG_L ra, 1*SZREG(sp) + REG_L s0, 8*SZREG(sp) + REG_L a0, 10*SZREG(sp) + REG_L a1, 11*SZREG(sp) + addi sp, sp, ABI_SIZE_ON_STACK .endm SYM_TYPED_FUNC_START(ftrace_stub)arch/s390/Kconfig +1
-1@@ -184,7 +184,7 @@ config S390 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/s390/include/asm/ftrace.h +9
-17@@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *return NULL ; } -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long gpr2; -unsigned long fp; -}; - -static __always_inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->gpr2; -} - -static __always_inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ -static __always_inline unsigned long ftrace_regs_get_instruction_pointer (const struct ftrace_regs *fregs) {@@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (struct ftrace_regs *fregs) +{ +unsigned long *sp; + + sp = (void *)ftrace_regs_get_stack_pointer (fregs); +return sp[0 ];/* return backchain */ +} +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS /* * When an ftrace registered caller is tracing a function that is arch/s390/kernel/asm-offsets.c +0
-6@@ -179,12 +179,6 @@ int main(void)DEFINE (OLDMEM_SIZE ,PARMAREA + offsetof(struct parmarea , oldmem_size));DEFINE (COMMAND_LINE ,PARMAREA + offsetof(struct parmarea , command_line));DEFINE (MAX_COMMAND_LINE_SIZE ,PARMAREA + offsetof(struct parmarea , max_command_line_size)); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -/* function graph return value tracing */ -OFFSET (__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2); -OFFSET (__FGRAPH_RET_FP, fgraph_ret_regs, fp); -DEFINE (__FGRAPH_RET_SIZE,sizeof (struct fgraph_ret_regs )); -#endif OFFSET (__FTRACE_REGS_PT_REGS, ftrace_regs, regs);DEFINE (__FTRACE_REGS_SIZE,sizeof (struct ftrace_regs ));arch/s390/kernel/mcount.S +5
-4@@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common) SYM_FUNC_START(return_to_handler) stmg %r2,%r5,32(%r15) lgr %r1,%r15 - aghi %r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE) +# Allocate ftrace_regs + backchain on the stack + aghi %r15,-STACK_FRAME_SIZE_FREGS stg %r1,__SF_BACKCHAIN(%r15) la %r3,STACK_FRAME_OVERHEAD(%r15) - stg %r1,__FGRAPH_RET_FP(%r3) - stg %r2,__FGRAPH_RET_GPR2(%r3) + stg %r2,(__SF_GPRS+2*8)(%r15) + stg %r15,(__SF_GPRS+15*8)(%r15) lgr %r2,%r3 brasl %r14,ftrace_return_to_handler - aghi %r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE + aghi %r15,STACK_FRAME_SIZE_FREGS lgr %r14,%r2 lmg %r2,%r5,32(%r15) BR_EX %r14arch/x86/Kconfig +1
-1@@ -228,7 +228,7 @@ config X86 select HAVE_GUP_FAST select HAVE_FENTRY if X86_64 || DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER if X86_32 || (X86_64 && DYNAMIC_FTRACE) select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/x86/include/asm/ftrace.h +2
-20@@ -64,6 +64,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@@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)#endif /* !COMPILE_OFFSETS */ #endif /* !__ASSEMBLY__ */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long ax; -unsigned long dx; -unsigned long bp; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->ax; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->bp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_X86_FTRACE_H */ arch/x86/kernel/ftrace_32.S +9
-6@@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__) SYM_FUNC_END(__fentry__) EXPORT_SYMBOL(__fentry__) +#define FRAME_SIZE PT_OLDSS+4 + SYM_CODE_START(ftrace_caller) #ifdef CONFIG_FRAME_POINTER@@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller) .globl return_to_handler return_to_handler: - pushl $0 - pushl %edx - pushl %eax + subl $(FRAME_SIZE), %esp + movl $0, PT_EBP(%esp) + movl %edx, PT_EDX(%esp) + movl %eax, PT_EAX(%esp) movl %esp, %eax call ftrace_return_to_handler movl %eax, %ecx - popl %eax - popl %edx - addl $4, %esp # skip ebp + movl %eax, PT_EAX(%esp) + movl %edx, PT_EDX(%esp) + addl $(FRAME_SIZE), %esp JMP_NOSPEC ecx #endifarch/x86/kernel/ftrace_64.S +9
-8@@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED ANNOTATE_NOENDBR - subq $24, %rsp - /* Save the return values */ - movq %rax, (%rsp) - movq %rdx, 8(%rsp) - movq %rbp, 16(%rsp) + /* Save ftrace_regs for function exit context */ + subq $(FRAME_SIZE), %rsp + + movq %rax, RAX(%rsp) + movq %rdx, RDX(%rsp) + movq %rbp, RBP(%rsp) movq %rsp, %rdi call ftrace_return_to_handler movq %rax, %rdi - movq 8(%rsp), %rdx - movq (%rsp), %rax + movq RDX(%rsp), %rdx + movq RAX(%rsp), %rax - addq $24, %rsp + addq $(FRAME_SIZE), %rsp /* * Jump back to the old return address. This cannot be JMP_NOSPEC rdi * since IBT would demand that contain ENDBR, which simply isn't so forinclude/linux/ftrace.h +11
-3@@ -43,9 +43,8 @@ struct dyn_ftrace;char *arch_ftrace_match_adjust (char *str,const char *search); -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -struct fgraph_ret_regs ; -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs); +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs);#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer);#endif @@ -134,6 +133,13 @@ extern int ftrace_enabled;* Also, architecture dependent fields can be used for internal process. * (e.g. orig_ax on x86_64) * +* Basically, ftrace_regs stores the registers related to the context. +* On function entry, registers for function parameters and hooking the +* function call are stored, and on function exit, registers for function +* return value and frame pointers are stored. +* +* And also, it dpends on the context that which registers are restored +* from the ftrace_regs. * On the function entry, those registers will be restored except for * the stack pointer, so that user can change the function parameters * and instruction pointer (e.g. live patching.) @@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs) override_function_with_return(ftrace_get_regs(fregs))#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +#define ftrace_regs_get_frame_pointer (fregs) \ + frame_pointer(&(fregs)->regs)#endif typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,kernel/trace/Kconfig +2
-2@@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER help See Documentation/trace/ftrace-design.rst -config HAVE_FUNCTION_GRAPH_RETVAL +config HAVE_FUNCTION_GRAPH_FREGS bool config HAVE_DYNAMIC_FTRACE@@ -232,7 +232,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_FREGS depends on FUNCTION_GRAPH_TRACER default n helpkernel/trace/fgraph.c +9
-12@@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = { .notifier_call = ftrace_suspend_notifier_call, }; -/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */ -struct fgraph_ret_regs ; -/* * 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, -unsigned long frame_pointer) +static inline unsigned long +__ftrace_return_to_handler (struct ftrace_regs *fregs,unsigned long frame_pointer) {struct ftrace_ret_stack *ret_stack;struct ftrace_graph_ret trace;@@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs trace.rettime =trace_clock_local ();#ifdef CONFIG_FUNCTION_GRAPH_RETVAL - trace.retval =fgraph_ret_regs_return_value (ret_regs); + trace.retval =ftrace_regs_get_return_value (fregs);#endif bitmap =get_bitmap_bits (current, offset);@@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs }/* -* After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can -* leave only ftrace_return_to_handler(ret_regs). +* After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can +* leave only ftrace_return_to_handler(fregs). */ -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs) +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs) { -return __ftrace_return_to_handler (ret_regs, -fgraph_ret_regs_frame_pointer (ret_regs)); +return __ftrace_return_to_handler (fregs, +ftrace_regs_get_frame_pointer (fregs)); }#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer)
Will Deacon
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Quoted 11 lines · Steven Rostedt
Quoted 8 lines · On Fri, 13 Sep 2024 00:08:51 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Use
ftrace_regsinstead offgraph_ret_regsfor tracing return value onfunction_graphtracer because of simplifying the callback interface.The
CONFIG_HAVE_FUNCTION_GRAPH_RETVALis also replaced byCONFIG_HAVE_FUNCTION_GRAPH_FREGS.
Quoted 9 lines
Quoted 9 lines
Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Newly added.--- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 23 ++++++----------------- arch/arm64/kernel/asm-offsets.c | 12 ------------ arch/arm64/kernel/entry-ftrace.S | 32 ++++++++++++++++++--------------
The arm64 part looks good to me, although passing a partially-populated struct out of asm feels like it's going to cause us hard-to-debug problems down the line if any of those extra fields get used. How hard would it be to poison the unpopulated members of 'ftrace_regs'?
Will
Steven Rostedt
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Quoted 4 lines · On Tue, 17 Sep 2024 10:55:39 +0100 Will Deacon <[email protected]> wrote:
The arm64 part looks good to me, although passing a partially-populated struct out of asm feels like it's going to cause us hard-to-debug problems down the line if any of those extra fields get used. How hard would it be to poison the unpopulated members of '
ftrace_regs'?
The purpose of creating ftrace_regs was to allow a partially populated pt_regs to be sent around, as Thomas Gleixner and Peter Zijlstra were against using pt_regs that were not fully populated. Hence, I created "ftrace_regs" for this purpose.
ftrace_regs should never be accessed via its internal elements but only with its accessor functions, as depending on the arch or functionality used, the content of the structure should never be trusted. The accessor functions will do all the verification needed.
I may add some compiler hacks to enforce this. Something like:
struct ftrace_regs {
void *nothing_to_see_here;
};And then change the arch code to be something like:
// in arch/arm64/include/asm/ftrace.h:
struct arch_ftrace_regs {
/* x0 - x8 */
unsigned long regs[9];#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
unsigned long direct_tramp;
#else
unsigned long __unused;#endif
unsigned long fp;
unsigned long lr;
unsigned long sp;
unsigned long pc;
};#define get_arch_ftrace_regs(fregs) ((struct arch_ftrace_regs *)(fregs))
static __always_inline void ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,
unsigned long pc)
{
struct arch_ftrace_regs *afregs = get_ftrace_regs(fregs);
afregs->pc = pc;
}-- Steve
Masami Hiramatsu (Google)
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Quoted 18 lines · On Mon, 30 Sep 2024 14:55:48 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 4 lines · On Tue, 17 Sep 2024 10:55:39 +0100 Will Deacon <[email protected]> wrote:
The arm64 part looks good to me, although passing a partially-populated struct out of asm feels like it's going to cause us hard-to-debug problems down the line if any of those extra fields get used. How hard would it be to poison the unpopulated members of '
ftrace_regs'?The purpose of creating
ftrace_regswas to allow a partially populatedpt_regsto be sent around, as Thomas Gleixner and Peter Zijlstra were against usingpt_regsthat were not fully populated. Hence, I created "ftrace_regs" for this purpose.
ftrace_regsshould never be accessed via its internal elements but only with its accessor functions, as depending on the arch or functionality used, the content of the structure should never be trusted. The accessor functions will do all the verification needed.
Quoted 5 lines
I may add some compiler hacks to enforce this. Something like:
struct ftrace_regs { void *nothing_to_see_here; };
Yeah, OK. But sizeof(fregs) may be changed. (Shouldn't we do too?)
Quoted 13 lines
And then change the arch code to be something like:
// in
arch/arm64/include/asm/ftrace.h:struct arch_ftrace_regs { /* x0 - x8 */ unsigned long regs[9];#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS unsigned long direct_tramp; #else unsigned long __unused;
Quoted 8 lines
#endif
unsigned long fp; unsigned long lr; unsigned long sp; unsigned long pc; };
And if it is pt_regs compatible,
#define arch_ftrace_regs pt_regs
?
Quoted 6 lines
#define
get_arch_ftrace_regs(fregs)((structarch_ftrace_regs*)(fregs))static
__always_inlinevoid ftrace_regs_set_instruction_pointer(structftrace_regs*fregs,
unsigned long pc)
Quoted 7 lines
{ struct arch_ftrace_regs *afregs = get_ftrace_regs(fregs); afregs->pc = pc; }-- Steve
Thanks,
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Steven Rostedt
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Quoted 8 lines · On Wed, 2 Oct 2024 08:10:37 +0900 Masami Hiramatsu (Google)…
Quoted 6 lines
I may add some compiler hacks to enforce this. Something like:
struct ftrace_regs { void *nothing_to_see_here; };Yeah, OK. But sizeof(fregs) may be changed. (Shouldn't we do too?)
Honestly, I don't think anything should be doing a sizeof(struct ftrace_regs)
Heck, perhaps we should make it totally zero!
struct ftrace_regs {
long nothing_here[];};
If someone needs to allocate, then we could provide a:
ftrace_regs_size()
helper function.
Quoted 23 lines
Quoted 14 lines
And then change the arch code to be something like:
// in
arch/arm64/include/asm/ftrace.h:struct arch_ftrace_regs { /* x0 - x8 */ unsigned long regs[9];#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS unsigned long direct_tramp; #else unsigned long __unused;Quoted 8 lines
#endif
unsigned long fp; unsigned long lr; unsigned long sp; unsigned long pc; };
Quoted 6 lines
And if it is
pt_regscompatible,#define
arch_ftrace_regspt_regs?
Only if it is fully pt_regs compatible.
-- Steve
Masami Hiramatsu (Google)
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Quoted 17 lines · On Tue, 1 Oct 2024 19:32:34 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 8 lines · On Wed, 2 Oct 2024 08:10:37 +0900 Masami Hiramatsu (Google)…
Quoted 6 lines
I may add some compiler hacks to enforce this. Something like:
struct ftrace_regs { void *nothing_to_see_here; };Yeah, OK. But sizeof(fregs) may be changed. (Shouldn't we do too?)
Honestly, I don't think anything should be doing a
sizeof(struct ftrace_regs)Heck, perhaps we should make it totally zero!
struct ftrace_regs { long nothing_here[];
Quoted 7 lines
};
If someone needs to allocate, then we could provide a:
ftrace_regs_size()helper function.
Ah, Indeed.
Quoted 24 lines
Quoted 24 lines
Quoted 23 lines
And then change the arch code to be something like:
// in
arch/arm64/include/asm/ftrace.h:struct arch_ftrace_regs { /* x0 - x8 */ unsigned long regs[9];#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS unsigned long direct_tramp; #else unsigned long __unused;#endif
unsigned long fp; unsigned long lr; unsigned long sp; unsigned long pc; };
Quoted 8 lines
Quoted 6 lines
And if it is
pt_regscompatible,#define
arch_ftrace_regspt_regs?
Only if it is fully
pt_regscompatible.
Yeah, OK, this is good idea.
Thank you,
Quoted 2 lines
-- Steve
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Steven Rostedt
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the RISC-V maintainers for this patch?
Thanks!
-- Steve
Quoted 739 lines · On Fri, 13 Sep 2024 00:08:51 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Use
ftrace_regsinstead offgraph_ret_regsfor tracing return value onfunction_graphtracer because of simplifying the callback interface.The
CONFIG_HAVE_FUNCTION_GRAPH_RETVALis also replaced byCONFIG_HAVE_FUNCTION_GRAPH_FREGS.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Newly added.--- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 23 ++++++----------------- arch/arm64/kernel/asm-offsets.c | 12 ------------ arch/arm64/kernel/entry-ftrace.S | 32 ++++++++++++++++++-------------- arch/loongarch/Kconfig | 2 +- arch/loongarch/include/asm/ftrace.h | 24 ++---------------------- arch/loongarch/kernel/asm-offsets.c | 12 ------------ arch/loongarch/kernel/mcount.S | 17 ++++++++++------- arch/loongarch/kernel/mcount_dyn.S | 14 +++++++------- arch/riscv/Kconfig | 2 +- arch/riscv/include/asm/ftrace.h | 26 +++++--------------------- arch/riscv/kernel/mcount.S | 24 +++++++++++++----------- arch/s390/Kconfig | 2 +- arch/s390/include/asm/ftrace.h | 26 +++++++++----------------- arch/s390/kernel/asm-offsets.c | 6 ------ arch/s390/kernel/mcount.S | 9 +++++---- arch/x86/Kconfig | 2 +- arch/x86/include/asm/ftrace.h | 22 ++-------------------- arch/x86/kernel/ftrace_32.S | 15 +++++++++------ arch/x86/kernel/ftrace_64.S | 17 +++++++++-------- include/linux/ftrace.h | 14 +++++++++++--- kernel/trace/Kconfig | 4 ++-- kernel/trace/fgraph.c | 21 +++++++++------------ 23 files changed, 122 insertions(+), 205 deletions(-)arch/arm64/Kconfig +1
-0@@ -211,6 +211,7 @@ config ARM64 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_ERROR_INJECTION + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_RETVAL select HAVE_GCC_PLUGINSarch/arm64/include/asm/ftrace.h +6
-17@@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs) fregs->pc = fregs->lr; } +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->fp; +} +int ftrace_regs_query_register_offset (const char *name);int ftrace_init_nop (struct module *mod,struct dyn_ftrace *rec);@@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,#ifndef __ASSEMBLY__#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* x0 - x7 */ -unsigned long regs[8 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -}void prepare_ftrace_return (unsigned long self_addr,unsigned long *parent,unsigned long frame_pointer);arch/arm64/kernel/asm-offsets.c +0
-12@@ -201,18 +201,6 @@ int main(void)DEFINE (FTRACE_OPS_FUNC , offsetof(struct ftrace_ops , func));#endif BLANK (); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - DEFINE(FGRET_REGS_X0, offsetof(struct fgraph_ret_regs, regs[0])); - DEFINE(FGRET_REGS_X1, offsetof(struct fgraph_ret_regs, regs[1])); - DEFINE(FGRET_REGS_X2, offsetof(struct fgraph_ret_regs, regs[2])); - DEFINE(FGRET_REGS_X3, offsetof(struct fgraph_ret_regs, regs[3])); - DEFINE(FGRET_REGS_X4, offsetof(struct fgraph_ret_regs, regs[4])); - DEFINE(FGRET_REGS_X5, offsetof(struct fgraph_ret_regs, regs[5])); - DEFINE(FGRET_REGS_X6, offsetof(struct fgraph_ret_regs, regs[6])); - DEFINE(FGRET_REGS_X7, offsetof(struct fgraph_ret_regs, regs[7])); -DEFINE (FGRET_REGS_FP , offsetof(struct fgraph_ret_regs , fp)); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -#endif #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS DEFINE (FTRACE_OPS_DIRECT_CALL , offsetof(struct ftrace_ops , direct_call));#endif arch/arm64/kernel/entry-ftrace.S +18
-14@@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph) * @fp is checked against the value passed by ftrace_graph_caller(). */ SYM_CODE_START(return_to_handler) - /* save return value regs */ - sub sp, sp, #FGRET_REGS_SIZE - stp x0, x1, [sp, #FGRET_REGS_X0] - stp x2, x3, [sp, #FGRET_REGS_X2] - stp x4, x5, [sp, #FGRET_REGS_X4] - stp x6, x7, [sp, #FGRET_REGS_X6] - str x29, [sp, #FGRET_REGS_FP] // parent's fp + /* Make room for ftrace_regs */ + sub sp, sp, #FREGS_SIZE + + /* Save return value regs */ + stp x0, x1, [sp, #FREGS_X0] + stp x2, x3, [sp, #FREGS_X2] + stp x4, x5, [sp, #FREGS_X4] + stp x6, x7, [sp, #FREGS_X6] + + /* Save the callsite's FP */ + str x29, [sp, #FREGS_FP] mov x0, sp - bl ftrace_return_to_handler // addr = ftrace_return_to_hander(regs); + bl ftrace_return_to_handler // addr = ftrace_return_to_hander(fregs); mov x30, x0 // restore the original return address - /* restore return value regs */ - ldp x0, x1, [sp, #FGRET_REGS_X0] - ldp x2, x3, [sp, #FGRET_REGS_X2] - ldp x4, x5, [sp, #FGRET_REGS_X4] - ldp x6, x7, [sp, #FGRET_REGS_X6] - add sp, sp, #FGRET_REGS_SIZE + /* Restore return value regs */ + ldp x0, x1, [sp, #FREGS_X0] + ldp x2, x3, [sp, #FREGS_X2] + ldp x4, x5, [sp, #FREGS_X4] + ldp x6, x7, [sp, #FREGS_X6] + add sp, sp, #FREGS_SIZE ret SYM_CODE_END(return_to_handler)arch/loongarch/Kconfig +1
-1@@ -131,7 +131,7 @@ config LOONGARCH select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/loongarch/include/asm/ftrace.h +2
-22@@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip) 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) \ + ((fregs)->regs.regs[22])#define ftrace_graph_func ftrace_graph_func void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,@@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)#endif /* CONFIG_FUNCTION_TRACER */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* a0 - a1 */ -unsigned long regs[2 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_LOONGARCH_FTRACE_H */ arch/loongarch/kernel/asm-offsets.c +0
-12@@ -279,18 +279,6 @@ static void __used output_pbe_defines(void) }#endif -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static void __used output_fgraph_ret_regs_defines (void ) -{ -COMMENT ("LoongArch fgraph_ret_regs offsets." ); -OFFSET (FGRET_REGS_A0 , fgraph_ret_regs, regs[0 ]); -OFFSET (FGRET_REGS_A1 , fgraph_ret_regs, regs[1 ]); -OFFSET (FGRET_REGS_FP , fgraph_ret_regs, fp); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -BLANK (); -} -#endif -static void __used output_kvm_defines (void ) {COMMENT ("KVM/LoongArch Specific offsets." );arch/loongarch/kernel/mcount.S +10
-7@@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller) SYM_FUNC_END(ftrace_graph_caller) SYM_FUNC_START(return_to_handler) - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + /* Save return value regs */ + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler@@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler) /* Restore the real parent address: a0 -> ra */ move ra, a0 - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + /* Restore return value regs */ + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE + jr ra SYM_FUNC_END(return_to_handler) #endif /* CONFIG_FUNCTION_GRAPH_TRACER */arch/loongarch/kernel/mcount_dyn.S +7
-7@@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED /* Save return value regs */ - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler move ra, a0 /* Restore return value regs */ - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE jr ra SYM_CODE_END(return_to_handler)arch/riscv/Kconfig +1
-1@@ -140,7 +140,7 @@ config RISCV select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_GRAPH_TRACER - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION select HAVE_EBPF_JIT if MMU select HAVE_GUP_FAST if MMUarch/riscv/include/asm/ftrace.h +5
-21@@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const structreturn fregs->sp; } +static __always_inline unsigned long ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->s0; +} +static __always_inline unsigned long ftrace_regs_get_argument (struct ftrace_regs *fregs,unsigned int n) {@@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi#endif /* CONFIG_DYNAMIC_FTRACE */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long a1; -unsigned long a0; -unsigned long s0; -unsigned long ra; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->a0; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->s0; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_RISCV_FTRACE_H */ arch/riscv/kernel/mcount.S +13
-11@@ -12,6 +12,8 @@ #include <asm/asm-offsets.h> #include <asm/ftrace.h> +#define ABI_SIZE_ON_STACK 80 + .text .macro SAVE_ABI_STATE@@ -26,12 +28,12 @@ * register if a0 was not saved. */ .macro SAVE_RET_ABI_STATE - addi sp, sp, -4*SZREG - REG_S s0, 2*SZREG(sp) - REG_S ra, 3*SZREG(sp) - REG_S a0, 1*SZREG(sp) - REG_S a1, 0*SZREG(sp) - addi s0, sp, 4*SZREG + addi sp, sp, -ABI_SIZE_ON_STACK + REG_S ra, 1*SZREG(sp) + REG_S s0, 8*SZREG(sp) + REG_S a0, 10*SZREG(sp) + REG_S a1, 11*SZREG(sp) + addi s0, sp, ABI_SIZE_ON_STACK .endm .macro RESTORE_ABI_STATE@@ -41,11 +43,11 @@ .endm .macro RESTORE_RET_ABI_STATE - REG_L ra, 3*SZREG(sp) - REG_L s0, 2*SZREG(sp) - REG_L a0, 1*SZREG(sp) - REG_L a1, 0*SZREG(sp) - addi sp, sp, 4*SZREG + REG_L ra, 1*SZREG(sp) + REG_L s0, 8*SZREG(sp) + REG_L a0, 10*SZREG(sp) + REG_L a1, 11*SZREG(sp) + addi sp, sp, ABI_SIZE_ON_STACK .endm SYM_TYPED_FUNC_START(ftrace_stub)arch/s390/Kconfig +1
-1@@ -184,7 +184,7 @@ config S390 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/s390/include/asm/ftrace.h +9
-17@@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *return NULL ; } -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long gpr2; -unsigned long fp; -}; - -static __always_inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->gpr2; -} - -static __always_inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ -static __always_inline unsigned long ftrace_regs_get_instruction_pointer (const struct ftrace_regs *fregs) {@@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (struct ftrace_regs *fregs) +{ +unsigned long *sp; + + sp = (void *)ftrace_regs_get_stack_pointer (fregs); +return sp[0 ];/* return backchain */ +} +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS /* * When an ftrace registered caller is tracing a function that is arch/s390/kernel/asm-offsets.c +0
-6@@ -179,12 +179,6 @@ int main(void)DEFINE (OLDMEM_SIZE ,PARMAREA + offsetof(struct parmarea , oldmem_size));DEFINE (COMMAND_LINE ,PARMAREA + offsetof(struct parmarea , command_line));DEFINE (MAX_COMMAND_LINE_SIZE ,PARMAREA + offsetof(struct parmarea , max_command_line_size)); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -/* function graph return value tracing */ -OFFSET (__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2); -OFFSET (__FGRAPH_RET_FP, fgraph_ret_regs, fp); -DEFINE (__FGRAPH_RET_SIZE,sizeof (struct fgraph_ret_regs )); -#endif OFFSET (__FTRACE_REGS_PT_REGS, ftrace_regs, regs);DEFINE (__FTRACE_REGS_SIZE,sizeof (struct ftrace_regs ));arch/s390/kernel/mcount.S +5
-4@@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common) SYM_FUNC_START(return_to_handler) stmg %r2,%r5,32(%r15) lgr %r1,%r15 - aghi %r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE) +# Allocate ftrace_regs + backchain on the stack + aghi %r15,-STACK_FRAME_SIZE_FREGS stg %r1,__SF_BACKCHAIN(%r15) la %r3,STACK_FRAME_OVERHEAD(%r15) - stg %r1,__FGRAPH_RET_FP(%r3) - stg %r2,__FGRAPH_RET_GPR2(%r3) + stg %r2,(__SF_GPRS+2*8)(%r15) + stg %r15,(__SF_GPRS+15*8)(%r15) lgr %r2,%r3 brasl %r14,ftrace_return_to_handler - aghi %r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE + aghi %r15,STACK_FRAME_SIZE_FREGS lgr %r14,%r2 lmg %r2,%r5,32(%r15) BR_EX %r14arch/x86/Kconfig +1
-1@@ -228,7 +228,7 @@ config X86 select HAVE_GUP_FAST select HAVE_FENTRY if X86_64 || DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER if X86_32 || (X86_64 && DYNAMIC_FTRACE) select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/x86/include/asm/ftrace.h +2
-20@@ -64,6 +64,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@@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)#endif /* !COMPILE_OFFSETS */ #endif /* !__ASSEMBLY__ */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long ax; -unsigned long dx; -unsigned long bp; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->ax; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->bp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_X86_FTRACE_H */ arch/x86/kernel/ftrace_32.S +9
-6@@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__) SYM_FUNC_END(__fentry__) EXPORT_SYMBOL(__fentry__) +#define FRAME_SIZE PT_OLDSS+4 + SYM_CODE_START(ftrace_caller) #ifdef CONFIG_FRAME_POINTER@@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller) .globl return_to_handler return_to_handler: - pushl $0 - pushl %edx - pushl %eax + subl $(FRAME_SIZE), %esp + movl $0, PT_EBP(%esp) + movl %edx, PT_EDX(%esp) + movl %eax, PT_EAX(%esp) movl %esp, %eax call ftrace_return_to_handler movl %eax, %ecx - popl %eax - popl %edx - addl $4, %esp # skip ebp + movl %eax, PT_EAX(%esp) + movl %edx, PT_EDX(%esp) + addl $(FRAME_SIZE), %esp JMP_NOSPEC ecx #endifarch/x86/kernel/ftrace_64.S +9
-8@@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED ANNOTATE_NOENDBR - subq $24, %rsp - /* Save the return values */ - movq %rax, (%rsp) - movq %rdx, 8(%rsp) - movq %rbp, 16(%rsp) + /* Save ftrace_regs for function exit context */ + subq $(FRAME_SIZE), %rsp + + movq %rax, RAX(%rsp) + movq %rdx, RDX(%rsp) + movq %rbp, RBP(%rsp) movq %rsp, %rdi call ftrace_return_to_handler movq %rax, %rdi - movq 8(%rsp), %rdx - movq (%rsp), %rax + movq RDX(%rsp), %rdx + movq RAX(%rsp), %rax - addq $24, %rsp + addq $(FRAME_SIZE), %rsp /* * Jump back to the old return address. This cannot be JMP_NOSPEC rdi * since IBT would demand that contain ENDBR, which simply isn't so forinclude/linux/ftrace.h +11
-3@@ -43,9 +43,8 @@ struct dyn_ftrace;char *arch_ftrace_match_adjust (char *str,const char *search); -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -struct fgraph_ret_regs ; -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs); +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs);#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer);#endif @@ -134,6 +133,13 @@ extern int ftrace_enabled;* Also, architecture dependent fields can be used for internal process. * (e.g. orig_ax on x86_64) * +* Basically, ftrace_regs stores the registers related to the context. +* On function entry, registers for function parameters and hooking the +* function call are stored, and on function exit, registers for function +* return value and frame pointers are stored. +* +* And also, it dpends on the context that which registers are restored +* from the ftrace_regs. * On the function entry, those registers will be restored except for * the stack pointer, so that user can change the function parameters * and instruction pointer (e.g. live patching.) @@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs) override_function_with_return(ftrace_get_regs(fregs))#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +#define ftrace_regs_get_frame_pointer (fregs) \ + frame_pointer(&(fregs)->regs)#endif typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,kernel/trace/Kconfig +2
-2@@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER help See Documentation/trace/ftrace-design.rst -config HAVE_FUNCTION_GRAPH_RETVAL +config HAVE_FUNCTION_GRAPH_FREGS bool config HAVE_DYNAMIC_FTRACE@@ -232,7 +232,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_FREGS depends on FUNCTION_GRAPH_TRACER default n helpkernel/trace/fgraph.c +9
-12@@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = { .notifier_call = ftrace_suspend_notifier_call, }; -/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */ -struct fgraph_ret_regs ; -/* * 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, -unsigned long frame_pointer) +static inline unsigned long +__ftrace_return_to_handler (struct ftrace_regs *fregs,unsigned long frame_pointer) {struct ftrace_ret_stack *ret_stack;struct ftrace_graph_ret trace;@@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs trace.rettime =trace_clock_local ();#ifdef CONFIG_FUNCTION_GRAPH_RETVAL - trace.retval =fgraph_ret_regs_return_value (ret_regs); + trace.retval =ftrace_regs_get_return_value (fregs);#endif bitmap =get_bitmap_bits (current, offset);@@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs }/* -* After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can -* leave only ftrace_return_to_handler(ret_regs). +* After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can +* leave only ftrace_return_to_handler(fregs). */ -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs) +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs) { -return __ftrace_return_to_handler (ret_regs, -fgraph_ret_regs_frame_pointer (ret_regs)); +return __ftrace_return_to_handler (fregs, +ftrace_regs_get_frame_pointer (fregs)); }#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer)
Steven Rostedt
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the S390 maintainers for this patch?
Thanks!
-- Steve
Quoted 739 lines · On Fri, 13 Sep 2024 00:08:51 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Use
ftrace_regsinstead offgraph_ret_regsfor tracing return value onfunction_graphtracer because of simplifying the callback interface.The
CONFIG_HAVE_FUNCTION_GRAPH_RETVALis also replaced byCONFIG_HAVE_FUNCTION_GRAPH_FREGS.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Newly added.--- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 23 ++++++----------------- arch/arm64/kernel/asm-offsets.c | 12 ------------ arch/arm64/kernel/entry-ftrace.S | 32 ++++++++++++++++++-------------- arch/loongarch/Kconfig | 2 +- arch/loongarch/include/asm/ftrace.h | 24 ++---------------------- arch/loongarch/kernel/asm-offsets.c | 12 ------------ arch/loongarch/kernel/mcount.S | 17 ++++++++++------- arch/loongarch/kernel/mcount_dyn.S | 14 +++++++------- arch/riscv/Kconfig | 2 +- arch/riscv/include/asm/ftrace.h | 26 +++++--------------------- arch/riscv/kernel/mcount.S | 24 +++++++++++++----------- arch/s390/Kconfig | 2 +- arch/s390/include/asm/ftrace.h | 26 +++++++++----------------- arch/s390/kernel/asm-offsets.c | 6 ------ arch/s390/kernel/mcount.S | 9 +++++---- arch/x86/Kconfig | 2 +- arch/x86/include/asm/ftrace.h | 22 ++-------------------- arch/x86/kernel/ftrace_32.S | 15 +++++++++------ arch/x86/kernel/ftrace_64.S | 17 +++++++++-------- include/linux/ftrace.h | 14 +++++++++++--- kernel/trace/Kconfig | 4 ++-- kernel/trace/fgraph.c | 21 +++++++++------------ 23 files changed, 122 insertions(+), 205 deletions(-)arch/arm64/Kconfig +1
-0@@ -211,6 +211,7 @@ config ARM64 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_ERROR_INJECTION + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_RETVAL select HAVE_GCC_PLUGINSarch/arm64/include/asm/ftrace.h +6
-17@@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs) fregs->pc = fregs->lr; } +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->fp; +} +int ftrace_regs_query_register_offset (const char *name);int ftrace_init_nop (struct module *mod,struct dyn_ftrace *rec);@@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,#ifndef __ASSEMBLY__#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* x0 - x7 */ -unsigned long regs[8 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -}void prepare_ftrace_return (unsigned long self_addr,unsigned long *parent,unsigned long frame_pointer);arch/arm64/kernel/asm-offsets.c +0
-12@@ -201,18 +201,6 @@ int main(void)DEFINE (FTRACE_OPS_FUNC , offsetof(struct ftrace_ops , func));#endif BLANK (); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - DEFINE(FGRET_REGS_X0, offsetof(struct fgraph_ret_regs, regs[0])); - DEFINE(FGRET_REGS_X1, offsetof(struct fgraph_ret_regs, regs[1])); - DEFINE(FGRET_REGS_X2, offsetof(struct fgraph_ret_regs, regs[2])); - DEFINE(FGRET_REGS_X3, offsetof(struct fgraph_ret_regs, regs[3])); - DEFINE(FGRET_REGS_X4, offsetof(struct fgraph_ret_regs, regs[4])); - DEFINE(FGRET_REGS_X5, offsetof(struct fgraph_ret_regs, regs[5])); - DEFINE(FGRET_REGS_X6, offsetof(struct fgraph_ret_regs, regs[6])); - DEFINE(FGRET_REGS_X7, offsetof(struct fgraph_ret_regs, regs[7])); -DEFINE (FGRET_REGS_FP , offsetof(struct fgraph_ret_regs , fp)); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -#endif #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS DEFINE (FTRACE_OPS_DIRECT_CALL , offsetof(struct ftrace_ops , direct_call));#endif arch/arm64/kernel/entry-ftrace.S +18
-14@@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph) * @fp is checked against the value passed by ftrace_graph_caller(). */ SYM_CODE_START(return_to_handler) - /* save return value regs */ - sub sp, sp, #FGRET_REGS_SIZE - stp x0, x1, [sp, #FGRET_REGS_X0] - stp x2, x3, [sp, #FGRET_REGS_X2] - stp x4, x5, [sp, #FGRET_REGS_X4] - stp x6, x7, [sp, #FGRET_REGS_X6] - str x29, [sp, #FGRET_REGS_FP] // parent's fp + /* Make room for ftrace_regs */ + sub sp, sp, #FREGS_SIZE + + /* Save return value regs */ + stp x0, x1, [sp, #FREGS_X0] + stp x2, x3, [sp, #FREGS_X2] + stp x4, x5, [sp, #FREGS_X4] + stp x6, x7, [sp, #FREGS_X6] + + /* Save the callsite's FP */ + str x29, [sp, #FREGS_FP] mov x0, sp - bl ftrace_return_to_handler // addr = ftrace_return_to_hander(regs); + bl ftrace_return_to_handler // addr = ftrace_return_to_hander(fregs); mov x30, x0 // restore the original return address - /* restore return value regs */ - ldp x0, x1, [sp, #FGRET_REGS_X0] - ldp x2, x3, [sp, #FGRET_REGS_X2] - ldp x4, x5, [sp, #FGRET_REGS_X4] - ldp x6, x7, [sp, #FGRET_REGS_X6] - add sp, sp, #FGRET_REGS_SIZE + /* Restore return value regs */ + ldp x0, x1, [sp, #FREGS_X0] + ldp x2, x3, [sp, #FREGS_X2] + ldp x4, x5, [sp, #FREGS_X4] + ldp x6, x7, [sp, #FREGS_X6] + add sp, sp, #FREGS_SIZE ret SYM_CODE_END(return_to_handler)arch/loongarch/Kconfig +1
-1@@ -131,7 +131,7 @@ config LOONGARCH select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/loongarch/include/asm/ftrace.h +2
-22@@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip) 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) \ + ((fregs)->regs.regs[22])#define ftrace_graph_func ftrace_graph_func void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,@@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)#endif /* CONFIG_FUNCTION_TRACER */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* a0 - a1 */ -unsigned long regs[2 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_LOONGARCH_FTRACE_H */ arch/loongarch/kernel/asm-offsets.c +0
-12@@ -279,18 +279,6 @@ static void __used output_pbe_defines(void) }#endif -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static void __used output_fgraph_ret_regs_defines (void ) -{ -COMMENT ("LoongArch fgraph_ret_regs offsets." ); -OFFSET (FGRET_REGS_A0 , fgraph_ret_regs, regs[0 ]); -OFFSET (FGRET_REGS_A1 , fgraph_ret_regs, regs[1 ]); -OFFSET (FGRET_REGS_FP , fgraph_ret_regs, fp); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -BLANK (); -} -#endif -static void __used output_kvm_defines (void ) {COMMENT ("KVM/LoongArch Specific offsets." );arch/loongarch/kernel/mcount.S +10
-7@@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller) SYM_FUNC_END(ftrace_graph_caller) SYM_FUNC_START(return_to_handler) - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + /* Save return value regs */ + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler@@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler) /* Restore the real parent address: a0 -> ra */ move ra, a0 - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + /* Restore return value regs */ + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE + jr ra SYM_FUNC_END(return_to_handler) #endif /* CONFIG_FUNCTION_GRAPH_TRACER */arch/loongarch/kernel/mcount_dyn.S +7
-7@@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED /* Save return value regs */ - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler move ra, a0 /* Restore return value regs */ - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE jr ra SYM_CODE_END(return_to_handler)arch/riscv/Kconfig +1
-1@@ -140,7 +140,7 @@ config RISCV select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_GRAPH_TRACER - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION select HAVE_EBPF_JIT if MMU select HAVE_GUP_FAST if MMUarch/riscv/include/asm/ftrace.h +5
-21@@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const structreturn fregs->sp; } +static __always_inline unsigned long ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->s0; +} +static __always_inline unsigned long ftrace_regs_get_argument (struct ftrace_regs *fregs,unsigned int n) {@@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi#endif /* CONFIG_DYNAMIC_FTRACE */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long a1; -unsigned long a0; -unsigned long s0; -unsigned long ra; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->a0; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->s0; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_RISCV_FTRACE_H */ arch/riscv/kernel/mcount.S +13
-11@@ -12,6 +12,8 @@ #include <asm/asm-offsets.h> #include <asm/ftrace.h> +#define ABI_SIZE_ON_STACK 80 + .text .macro SAVE_ABI_STATE@@ -26,12 +28,12 @@ * register if a0 was not saved. */ .macro SAVE_RET_ABI_STATE - addi sp, sp, -4*SZREG - REG_S s0, 2*SZREG(sp) - REG_S ra, 3*SZREG(sp) - REG_S a0, 1*SZREG(sp) - REG_S a1, 0*SZREG(sp) - addi s0, sp, 4*SZREG + addi sp, sp, -ABI_SIZE_ON_STACK + REG_S ra, 1*SZREG(sp) + REG_S s0, 8*SZREG(sp) + REG_S a0, 10*SZREG(sp) + REG_S a1, 11*SZREG(sp) + addi s0, sp, ABI_SIZE_ON_STACK .endm .macro RESTORE_ABI_STATE@@ -41,11 +43,11 @@ .endm .macro RESTORE_RET_ABI_STATE - REG_L ra, 3*SZREG(sp) - REG_L s0, 2*SZREG(sp) - REG_L a0, 1*SZREG(sp) - REG_L a1, 0*SZREG(sp) - addi sp, sp, 4*SZREG + REG_L ra, 1*SZREG(sp) + REG_L s0, 8*SZREG(sp) + REG_L a0, 10*SZREG(sp) + REG_L a1, 11*SZREG(sp) + addi sp, sp, ABI_SIZE_ON_STACK .endm SYM_TYPED_FUNC_START(ftrace_stub)arch/s390/Kconfig +1
-1@@ -184,7 +184,7 @@ config S390 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/s390/include/asm/ftrace.h +9
-17@@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *return NULL ; } -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long gpr2; -unsigned long fp; -}; - -static __always_inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->gpr2; -} - -static __always_inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ -static __always_inline unsigned long ftrace_regs_get_instruction_pointer (const struct ftrace_regs *fregs) {@@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (struct ftrace_regs *fregs) +{ +unsigned long *sp; + + sp = (void *)ftrace_regs_get_stack_pointer (fregs); +return sp[0 ];/* return backchain */ +} +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS /* * When an ftrace registered caller is tracing a function that is arch/s390/kernel/asm-offsets.c +0
-6@@ -179,12 +179,6 @@ int main(void)DEFINE (OLDMEM_SIZE ,PARMAREA + offsetof(struct parmarea , oldmem_size));DEFINE (COMMAND_LINE ,PARMAREA + offsetof(struct parmarea , command_line));DEFINE (MAX_COMMAND_LINE_SIZE ,PARMAREA + offsetof(struct parmarea , max_command_line_size)); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -/* function graph return value tracing */ -OFFSET (__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2); -OFFSET (__FGRAPH_RET_FP, fgraph_ret_regs, fp); -DEFINE (__FGRAPH_RET_SIZE,sizeof (struct fgraph_ret_regs )); -#endif OFFSET (__FTRACE_REGS_PT_REGS, ftrace_regs, regs);DEFINE (__FTRACE_REGS_SIZE,sizeof (struct ftrace_regs ));arch/s390/kernel/mcount.S +5
-4@@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common) SYM_FUNC_START(return_to_handler) stmg %r2,%r5,32(%r15) lgr %r1,%r15 - aghi %r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE) +# Allocate ftrace_regs + backchain on the stack + aghi %r15,-STACK_FRAME_SIZE_FREGS stg %r1,__SF_BACKCHAIN(%r15) la %r3,STACK_FRAME_OVERHEAD(%r15) - stg %r1,__FGRAPH_RET_FP(%r3) - stg %r2,__FGRAPH_RET_GPR2(%r3) + stg %r2,(__SF_GPRS+2*8)(%r15) + stg %r15,(__SF_GPRS+15*8)(%r15) lgr %r2,%r3 brasl %r14,ftrace_return_to_handler - aghi %r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE + aghi %r15,STACK_FRAME_SIZE_FREGS lgr %r14,%r2 lmg %r2,%r5,32(%r15) BR_EX %r14arch/x86/Kconfig +1
-1@@ -228,7 +228,7 @@ config X86 select HAVE_GUP_FAST select HAVE_FENTRY if X86_64 || DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER if X86_32 || (X86_64 && DYNAMIC_FTRACE) select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/x86/include/asm/ftrace.h +2
-20@@ -64,6 +64,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@@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)#endif /* !COMPILE_OFFSETS */ #endif /* !__ASSEMBLY__ */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long ax; -unsigned long dx; -unsigned long bp; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->ax; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->bp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_X86_FTRACE_H */ arch/x86/kernel/ftrace_32.S +9
-6@@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__) SYM_FUNC_END(__fentry__) EXPORT_SYMBOL(__fentry__) +#define FRAME_SIZE PT_OLDSS+4 + SYM_CODE_START(ftrace_caller) #ifdef CONFIG_FRAME_POINTER@@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller) .globl return_to_handler return_to_handler: - pushl $0 - pushl %edx - pushl %eax + subl $(FRAME_SIZE), %esp + movl $0, PT_EBP(%esp) + movl %edx, PT_EDX(%esp) + movl %eax, PT_EAX(%esp) movl %esp, %eax call ftrace_return_to_handler movl %eax, %ecx - popl %eax - popl %edx - addl $4, %esp # skip ebp + movl %eax, PT_EAX(%esp) + movl %edx, PT_EDX(%esp) + addl $(FRAME_SIZE), %esp JMP_NOSPEC ecx #endifarch/x86/kernel/ftrace_64.S +9
-8@@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED ANNOTATE_NOENDBR - subq $24, %rsp - /* Save the return values */ - movq %rax, (%rsp) - movq %rdx, 8(%rsp) - movq %rbp, 16(%rsp) + /* Save ftrace_regs for function exit context */ + subq $(FRAME_SIZE), %rsp + + movq %rax, RAX(%rsp) + movq %rdx, RDX(%rsp) + movq %rbp, RBP(%rsp) movq %rsp, %rdi call ftrace_return_to_handler movq %rax, %rdi - movq 8(%rsp), %rdx - movq (%rsp), %rax + movq RDX(%rsp), %rdx + movq RAX(%rsp), %rax - addq $24, %rsp + addq $(FRAME_SIZE), %rsp /* * Jump back to the old return address. This cannot be JMP_NOSPEC rdi * since IBT would demand that contain ENDBR, which simply isn't so forinclude/linux/ftrace.h +11
-3@@ -43,9 +43,8 @@ struct dyn_ftrace;char *arch_ftrace_match_adjust (char *str,const char *search); -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -struct fgraph_ret_regs ; -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs); +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs);#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer);#endif @@ -134,6 +133,13 @@ extern int ftrace_enabled;* Also, architecture dependent fields can be used for internal process. * (e.g. orig_ax on x86_64) * +* Basically, ftrace_regs stores the registers related to the context. +* On function entry, registers for function parameters and hooking the +* function call are stored, and on function exit, registers for function +* return value and frame pointers are stored. +* +* And also, it dpends on the context that which registers are restored +* from the ftrace_regs. * On the function entry, those registers will be restored except for * the stack pointer, so that user can change the function parameters * and instruction pointer (e.g. live patching.) @@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs) override_function_with_return(ftrace_get_regs(fregs))#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +#define ftrace_regs_get_frame_pointer (fregs) \ + frame_pointer(&(fregs)->regs)#endif typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,kernel/trace/Kconfig +2
-2@@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER help See Documentation/trace/ftrace-design.rst -config HAVE_FUNCTION_GRAPH_RETVAL +config HAVE_FUNCTION_GRAPH_FREGS bool config HAVE_DYNAMIC_FTRACE@@ -232,7 +232,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_FREGS depends on FUNCTION_GRAPH_TRACER default n helpkernel/trace/fgraph.c +9
-12@@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = { .notifier_call = ftrace_suspend_notifier_call, }; -/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */ -struct fgraph_ret_regs ; -/* * 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, -unsigned long frame_pointer) +static inline unsigned long +__ftrace_return_to_handler (struct ftrace_regs *fregs,unsigned long frame_pointer) {struct ftrace_ret_stack *ret_stack;struct ftrace_graph_ret trace;@@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs trace.rettime =trace_clock_local ();#ifdef CONFIG_FUNCTION_GRAPH_RETVAL - trace.retval =fgraph_ret_regs_return_value (ret_regs); + trace.retval =ftrace_regs_get_return_value (fregs);#endif bitmap =get_bitmap_bits (current, offset);@@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs }/* -* After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can -* leave only ftrace_return_to_handler(ret_regs). +* After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can +* leave only ftrace_return_to_handler(fregs). */ -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs) +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs) { -return __ftrace_return_to_handler (ret_regs, -fgraph_ret_regs_frame_pointer (ret_regs)); +return __ftrace_return_to_handler (fregs, +ftrace_regs_get_frame_pointer (fregs)); }#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer)
Re: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Quoted 2 lines · Steven Rostedt
Can I get an Acked-by from the S390 maintainers for this patch?
...
Quoted 9 lines
Quoted 9 lines
(quoted diff) +9
-0+static __always_inline unsigned long +ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs) +{ + unsigned long *sp; + + sp = (void *)ftrace_regs_get_stack_pointer(fregs); + return sp[0]; /* return backchain */ +} +
...
Quoted 13 lines
Quoted 13 lines
arch/s390/kernel/mcount.S +2
-1@@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common) SYM_FUNC_START(return_to_handler) stmg %r2,%r5,32(%r15) lgr %r1,%r15 - aghi %r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE) +# Allocate ftrace_regs + backchain on the stack + aghi %r15,-STACK_FRAME_SIZE_FREGS stg %r1,__SF_BACKCHAIN(%r15) la %r3,STACK_FRAME_OVERHEAD(%r15)
Quoted 8 lines
arch/s390/kernel/mcount.S SYM_CODE_END(ftrace_common) · line 144
Quoted 8 lines
arch/s390/kernel/mcount.S +3
-3- stg %r1,__FGRAPH_RET_FP(%r3) - stg %r2,__FGRAPH_RET_GPR2(%r3) + stg %r2,(__SF_GPRS+2*8)(%r15) + stg %r15,(__SF_GPRS+15*8)(%r15) lgr %r2,%r3 brasl %r14,ftrace_return_to_handler - aghi %r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE + aghi %r15,STACK_FRAME_SIZE_FREGS
This does not pass the ftrace selftests. Please merge the patch below into this patch. With that:
arch/s390/include/asm/ftrace.h +1 -4
@@ -78,10 +78,7 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,static __always_inline unsigned long ftrace_regs_get_frame_pointer (struct ftrace_regs *fregs) { -unsigned long *sp; - - sp = (void *)ftrace_regs_get_stack_pointer (fregs); -return sp[0 ];/* return backchain */ +return ftrace_regs_get_stack_pointer (fregs); } static __always_inline unsigned long
arch/s390/kernel/mcount.S +4 -5
@@ -133,13 +133,12 @@ SYM_CODE_END(ftrace_common) SYM_FUNC_START(return_to_handler) stmg %r2,%r5,32(%r15) lgr %r1,%r15 -# Allocate ftrace_regs + backchain on the stack + # allocate ftrace_regs and stack frame for ftrace_return_to_handler aghi %r15,-STACK_FRAME_SIZE_FREGS stg %r1,__SF_BACKCHAIN(%r15) - la %r3,STACK_FRAME_OVERHEAD(%r15) - stg %r2,(__SF_GPRS+2*8)(%r15) - stg %r15,(__SF_GPRS+15*8)(%r15) - lgr %r2,%r3 + stg %r2,(STACK_FREGS_PTREGS_GPRS+2*8)(%r15) + stg %r1,(STACK_FREGS_PTREGS_GPRS+15*8)(%r15) + la %r2,STACK_FRAME_OVERHEAD(%r15) brasl %r14,ftrace_return_to_handler aghi %r15,STACK_FRAME_SIZE_FREGS lgr %r14,%r2
Steven Rostedt
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Quoted 4 lines · On Mon, 16 Sep 2024 14:16:56 +0200 Heiko Carstens <[email protected]> wrote:
This does not pass the ftrace selftests. Please merge the patch below into this patch. With that:
Acked-by Heiko Carstens <[email protected]>
Thank you very much, this is why I wanted arch maintainers acks before taking anything.
There may be other patches in this series that I didn't Cc everyone (yet). Did you look at the other patches? If not, I'll go and do the Cc. It's a manual process.
-- Steve
Heiko Carstens
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Quoted 6 lines · Steven Rostedt
Quoted 3 lines · On Mon, 16 Sep 2024 14:16:56 +0200 Heiko Carstens <[email protected]> wrote:
This does not pass the ftrace selftests. Please merge the patch below into this patch. With that:
Quoted 8 lines
Quoted 1 line
Acked-by Heiko Carstens <[email protected]>Thank you very much, this is why I wanted arch maintainers acks before taking anything.
There may be other patches in this series that I didn't Cc everyone (yet). Did you look at the other patches? If not, I'll go and do the Cc. It's a manual process.
I just had a quick look and tried Masami's git tree with the topic/fprobe-on-fgraph branch and tried the ftrace selftests, which resulted in the provided small fix.
But now that you ask, I can see that FPROBE is not available on s390 anymore with the full series, since HAVE_FTRACE_GRAPH_FUNC is not available.
Also the s390 variant of arch_ftrace_fill_perf_regs() seems to be incorrect.
Looks like some work is needed from our side.
Masami Hiramatsu (Google)
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
Quoted 12 lines · On Mon, 16 Sep 2024 20:59:05 +0200 Heiko Carstens <[email protected]> wrote:
Quoted 11 lines · Steven Rostedt
Quoted 4 lines · On Mon, 16 Sep 2024 14:16:56 +0200 Heiko Carstens <[email protected]> wrote:
This does not pass the ftrace selftests. Please merge the patch below into this patch. With that:
Acked-by Heiko Carstens <[email protected]>Thank you very much, this is why I wanted arch maintainers acks before taking anything.
Quoted 7 lines
Quoted 3 lines
There may be other patches in this series that I didn't Cc everyone (yet). Did you look at the other patches? If not, I'll go and do the Cc. It's a manual process.
I just had a quick look and tried Masami's git tree with the topic/fprobe-on-fgraph branch and tried the ftrace selftests, which resulted in the provided small fix.
Thanks, and sorry, my change on s390 was wrong. I confused that stack_frame can be a part of ftrace_regs. But at least in this moment, ftrace_regs should replace only fgraph_ret_regs.
Quoted 4 lines
But now that you ask, I can see that FPROBE is not available on s390 anymore with the full series, since
HAVE_FTRACE_GRAPH_FUNCis not available.
Right, this is required for implementing fprobe.
Quoted 3 lines
Also the s390 variant of
arch_ftrace_fill_perf_regs()seems to be incorrect.
Can you tell me what part will be wrong?
Thank you,
Quoted 2 lines
Looks like some work is needed from our side.
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Steven Rostedt
maintainerRe: [PATCH v14 04/19] function_graph: Replace fgraph_ret_regs with ftrace_regs
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the X86 maintainers for this patch?
Thanks!
-- Steve
Quoted 739 lines · On Fri, 13 Sep 2024 00:08:51 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Use
ftrace_regsinstead offgraph_ret_regsfor tracing return value onfunction_graphtracer because of simplifying the callback interface.The
CONFIG_HAVE_FUNCTION_GRAPH_RETVALis also replaced byCONFIG_HAVE_FUNCTION_GRAPH_FREGS.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Newly added.--- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 23 ++++++----------------- arch/arm64/kernel/asm-offsets.c | 12 ------------ arch/arm64/kernel/entry-ftrace.S | 32 ++++++++++++++++++-------------- arch/loongarch/Kconfig | 2 +- arch/loongarch/include/asm/ftrace.h | 24 ++---------------------- arch/loongarch/kernel/asm-offsets.c | 12 ------------ arch/loongarch/kernel/mcount.S | 17 ++++++++++------- arch/loongarch/kernel/mcount_dyn.S | 14 +++++++------- arch/riscv/Kconfig | 2 +- arch/riscv/include/asm/ftrace.h | 26 +++++--------------------- arch/riscv/kernel/mcount.S | 24 +++++++++++++----------- arch/s390/Kconfig | 2 +- arch/s390/include/asm/ftrace.h | 26 +++++++++----------------- arch/s390/kernel/asm-offsets.c | 6 ------ arch/s390/kernel/mcount.S | 9 +++++---- arch/x86/Kconfig | 2 +- arch/x86/include/asm/ftrace.h | 22 ++-------------------- arch/x86/kernel/ftrace_32.S | 15 +++++++++------ arch/x86/kernel/ftrace_64.S | 17 +++++++++-------- include/linux/ftrace.h | 14 +++++++++++--- kernel/trace/Kconfig | 4 ++-- kernel/trace/fgraph.c | 21 +++++++++------------ 23 files changed, 122 insertions(+), 205 deletions(-)arch/arm64/Kconfig +1
-0@@ -211,6 +211,7 @@ config ARM64 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_ERROR_INJECTION + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_RETVAL select HAVE_GCC_PLUGINSarch/arm64/include/asm/ftrace.h +6
-17@@ -126,6 +126,12 @@ ftrace_override_function_with_return(struct ftrace_regs *fregs) fregs->pc = fregs->lr; } +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->fp; +} +int ftrace_regs_query_register_offset (const char *name);int ftrace_init_nop (struct module *mod,struct dyn_ftrace *rec);@@ -183,23 +189,6 @@ static inline bool arch_syscall_match_sym_name(const char *sym,#ifndef __ASSEMBLY__#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* x0 - x7 */ -unsigned long regs[8 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -}void prepare_ftrace_return (unsigned long self_addr,unsigned long *parent,unsigned long frame_pointer);arch/arm64/kernel/asm-offsets.c +0
-12@@ -201,18 +201,6 @@ int main(void)DEFINE (FTRACE_OPS_FUNC , offsetof(struct ftrace_ops , func));#endif BLANK (); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - DEFINE(FGRET_REGS_X0, offsetof(struct fgraph_ret_regs, regs[0])); - DEFINE(FGRET_REGS_X1, offsetof(struct fgraph_ret_regs, regs[1])); - DEFINE(FGRET_REGS_X2, offsetof(struct fgraph_ret_regs, regs[2])); - DEFINE(FGRET_REGS_X3, offsetof(struct fgraph_ret_regs, regs[3])); - DEFINE(FGRET_REGS_X4, offsetof(struct fgraph_ret_regs, regs[4])); - DEFINE(FGRET_REGS_X5, offsetof(struct fgraph_ret_regs, regs[5])); - DEFINE(FGRET_REGS_X6, offsetof(struct fgraph_ret_regs, regs[6])); - DEFINE(FGRET_REGS_X7, offsetof(struct fgraph_ret_regs, regs[7])); -DEFINE (FGRET_REGS_FP , offsetof(struct fgraph_ret_regs , fp)); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -#endif #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS DEFINE (FTRACE_OPS_DIRECT_CALL , offsetof(struct ftrace_ops , direct_call));#endif arch/arm64/kernel/entry-ftrace.S +18
-14@@ -329,24 +329,28 @@ SYM_FUNC_END(ftrace_stub_graph) * @fp is checked against the value passed by ftrace_graph_caller(). */ SYM_CODE_START(return_to_handler) - /* save return value regs */ - sub sp, sp, #FGRET_REGS_SIZE - stp x0, x1, [sp, #FGRET_REGS_X0] - stp x2, x3, [sp, #FGRET_REGS_X2] - stp x4, x5, [sp, #FGRET_REGS_X4] - stp x6, x7, [sp, #FGRET_REGS_X6] - str x29, [sp, #FGRET_REGS_FP] // parent's fp + /* Make room for ftrace_regs */ + sub sp, sp, #FREGS_SIZE + + /* Save return value regs */ + stp x0, x1, [sp, #FREGS_X0] + stp x2, x3, [sp, #FREGS_X2] + stp x4, x5, [sp, #FREGS_X4] + stp x6, x7, [sp, #FREGS_X6] + + /* Save the callsite's FP */ + str x29, [sp, #FREGS_FP] mov x0, sp - bl ftrace_return_to_handler // addr = ftrace_return_to_hander(regs); + bl ftrace_return_to_handler // addr = ftrace_return_to_hander(fregs); mov x30, x0 // restore the original return address - /* restore return value regs */ - ldp x0, x1, [sp, #FGRET_REGS_X0] - ldp x2, x3, [sp, #FGRET_REGS_X2] - ldp x4, x5, [sp, #FGRET_REGS_X4] - ldp x6, x7, [sp, #FGRET_REGS_X6] - add sp, sp, #FGRET_REGS_SIZE + /* Restore return value regs */ + ldp x0, x1, [sp, #FREGS_X0] + ldp x2, x3, [sp, #FREGS_X2] + ldp x4, x5, [sp, #FREGS_X4] + ldp x6, x7, [sp, #FREGS_X6] + add sp, sp, #FREGS_SIZE ret SYM_CODE_END(return_to_handler)arch/loongarch/Kconfig +1
-1@@ -131,7 +131,7 @@ config LOONGARCH select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/loongarch/include/asm/ftrace.h +2
-22@@ -77,6 +77,8 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip) 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) \ + ((fregs)->regs.regs[22])#define ftrace_graph_func ftrace_graph_func void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,@@ -99,26 +101,4 @@ __arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)#endif /* CONFIG_FUNCTION_TRACER */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -/* a0 - a1 */ -unsigned long regs[2 ]; - -unsigned long fp; -unsigned long __unused; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->regs[0 ]; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_LOONGARCH_FTRACE_H */ arch/loongarch/kernel/asm-offsets.c +0
-12@@ -279,18 +279,6 @@ static void __used output_pbe_defines(void) }#endif -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -static void __used output_fgraph_ret_regs_defines (void ) -{ -COMMENT ("LoongArch fgraph_ret_regs offsets." ); -OFFSET (FGRET_REGS_A0 , fgraph_ret_regs, regs[0 ]); -OFFSET (FGRET_REGS_A1 , fgraph_ret_regs, regs[1 ]); -OFFSET (FGRET_REGS_FP , fgraph_ret_regs, fp); -DEFINE (FGRET_REGS_SIZE ,sizeof (struct fgraph_ret_regs )); -BLANK (); -} -#endif -static void __used output_kvm_defines (void ) {COMMENT ("KVM/LoongArch Specific offsets." );arch/loongarch/kernel/mcount.S +10
-7@@ -79,10 +79,11 @@ SYM_FUNC_START(ftrace_graph_caller) SYM_FUNC_END(ftrace_graph_caller) SYM_FUNC_START(return_to_handler) - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + /* Save return value regs */ + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler@@ -90,9 +91,11 @@ SYM_FUNC_START(return_to_handler) /* Restore the real parent address: a0 -> ra */ move ra, a0 - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + /* Restore return value regs */ + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE + jr ra SYM_FUNC_END(return_to_handler) #endif /* CONFIG_FUNCTION_GRAPH_TRACER */arch/loongarch/kernel/mcount_dyn.S +7
-7@@ -140,19 +140,19 @@ SYM_CODE_END(ftrace_graph_caller) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED /* Save return value regs */ - PTR_ADDI sp, sp, -FGRET_REGS_SIZE - PTR_S a0, sp, FGRET_REGS_A0 - PTR_S a1, sp, FGRET_REGS_A1 - PTR_S zero, sp, FGRET_REGS_FP + PTR_ADDI sp, sp, -PT_SIZE + PTR_S a0, sp, PT_R4 + PTR_S a1, sp, PT_R5 + PTR_S zero, sp, PT_R22 move a0, sp bl ftrace_return_to_handler move ra, a0 /* Restore return value regs */ - PTR_L a0, sp, FGRET_REGS_A0 - PTR_L a1, sp, FGRET_REGS_A1 - PTR_ADDI sp, sp, FGRET_REGS_SIZE + PTR_L a0, sp, PT_R4 + PTR_L a1, sp, PT_R5 + PTR_ADDI sp, sp, PT_SIZE jr ra SYM_CODE_END(return_to_handler)arch/riscv/Kconfig +1
-1@@ -140,7 +140,7 @@ config RISCV select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_GRAPH_TRACER - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION select HAVE_EBPF_JIT if MMU select HAVE_GUP_FAST if MMUarch/riscv/include/asm/ftrace.h +5
-21@@ -164,6 +164,11 @@ static __always_inline unsigned long ftrace_regs_get_stack_pointer(const structreturn fregs->sp; } +static __always_inline unsigned long ftrace_regs_get_frame_pointer (const struct ftrace_regs *fregs) +{ +return fregs->s0; +} +static __always_inline unsigned long ftrace_regs_get_argument (struct ftrace_regs *fregs,unsigned int n) {@@ -204,25 +209,4 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi#endif /* CONFIG_DYNAMIC_FTRACE */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long a1; -unsigned long a0; -unsigned long s0; -unsigned long ra; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->a0; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->s0; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_RISCV_FTRACE_H */ arch/riscv/kernel/mcount.S +13
-11@@ -12,6 +12,8 @@ #include <asm/asm-offsets.h> #include <asm/ftrace.h> +#define ABI_SIZE_ON_STACK 80 + .text .macro SAVE_ABI_STATE@@ -26,12 +28,12 @@ * register if a0 was not saved. */ .macro SAVE_RET_ABI_STATE - addi sp, sp, -4*SZREG - REG_S s0, 2*SZREG(sp) - REG_S ra, 3*SZREG(sp) - REG_S a0, 1*SZREG(sp) - REG_S a1, 0*SZREG(sp) - addi s0, sp, 4*SZREG + addi sp, sp, -ABI_SIZE_ON_STACK + REG_S ra, 1*SZREG(sp) + REG_S s0, 8*SZREG(sp) + REG_S a0, 10*SZREG(sp) + REG_S a1, 11*SZREG(sp) + addi s0, sp, ABI_SIZE_ON_STACK .endm .macro RESTORE_ABI_STATE@@ -41,11 +43,11 @@ .endm .macro RESTORE_RET_ABI_STATE - REG_L ra, 3*SZREG(sp) - REG_L s0, 2*SZREG(sp) - REG_L a0, 1*SZREG(sp) - REG_L a1, 0*SZREG(sp) - addi sp, sp, 4*SZREG + REG_L ra, 1*SZREG(sp) + REG_L s0, 8*SZREG(sp) + REG_L a0, 10*SZREG(sp) + REG_L a1, 11*SZREG(sp) + addi sp, sp, ABI_SIZE_ON_STACK .endm SYM_TYPED_FUNC_START(ftrace_stub)arch/s390/Kconfig +1
-1@@ -184,7 +184,7 @@ config S390 select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FUNCTION_GRAPH_RETVAL + select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/s390/include/asm/ftrace.h +9
-17@@ -49,23 +49,6 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *return NULL ; } -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long gpr2; -unsigned long fp; -}; - -static __always_inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->gpr2; -} - -static __always_inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->fp; -} -#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ -static __always_inline unsigned long ftrace_regs_get_instruction_pointer (const struct ftrace_regs *fregs) {@@ -92,6 +75,15 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs,#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +static __always_inline unsigned long +ftrace_regs_get_frame_pointer (struct ftrace_regs *fregs) +{ +unsigned long *sp; + + sp = (void *)ftrace_regs_get_stack_pointer (fregs); +return sp[0 ];/* return backchain */ +} +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS /* * When an ftrace registered caller is tracing a function that is arch/s390/kernel/asm-offsets.c +0
-6@@ -179,12 +179,6 @@ int main(void)DEFINE (OLDMEM_SIZE ,PARMAREA + offsetof(struct parmarea , oldmem_size));DEFINE (COMMAND_LINE ,PARMAREA + offsetof(struct parmarea , command_line));DEFINE (MAX_COMMAND_LINE_SIZE ,PARMAREA + offsetof(struct parmarea , max_command_line_size)); -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -/* function graph return value tracing */ -OFFSET (__FGRAPH_RET_GPR2, fgraph_ret_regs, gpr2); -OFFSET (__FGRAPH_RET_FP, fgraph_ret_regs, fp); -DEFINE (__FGRAPH_RET_SIZE,sizeof (struct fgraph_ret_regs )); -#endif OFFSET (__FTRACE_REGS_PT_REGS, ftrace_regs, regs);DEFINE (__FTRACE_REGS_SIZE,sizeof (struct ftrace_regs ));arch/s390/kernel/mcount.S +5
-4@@ -133,14 +133,15 @@ SYM_CODE_END(ftrace_common) SYM_FUNC_START(return_to_handler) stmg %r2,%r5,32(%r15) lgr %r1,%r15 - aghi %r15,-(STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE) +# Allocate ftrace_regs + backchain on the stack + aghi %r15,-STACK_FRAME_SIZE_FREGS stg %r1,__SF_BACKCHAIN(%r15) la %r3,STACK_FRAME_OVERHEAD(%r15) - stg %r1,__FGRAPH_RET_FP(%r3) - stg %r2,__FGRAPH_RET_GPR2(%r3) + stg %r2,(__SF_GPRS+2*8)(%r15) + stg %r15,(__SF_GPRS+15*8)(%r15) lgr %r2,%r3 brasl %r14,ftrace_return_to_handler - aghi %r15,STACK_FRAME_OVERHEAD+__FGRAPH_RET_SIZE + aghi %r15,STACK_FRAME_SIZE_FREGS lgr %r14,%r2 lmg %r2,%r5,32(%r15) BR_EX %r14arch/x86/Kconfig +1
-1@@ -228,7 +228,7 @@ config X86 select HAVE_GUP_FAST select HAVE_FENTRY if X86_64 || DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD - select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_GRAPH_FREGS if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER if X86_32 || (X86_64 && DYNAMIC_FTRACE) select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINSarch/x86/include/asm/ftrace.h +2
-20@@ -64,6 +64,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@@ -148,24 +150,4 @@ static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)#endif /* !COMPILE_OFFSETS */ #endif /* !__ASSEMBLY__ */ -#ifndef __ASSEMBLY__ -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -struct fgraph_ret_regs { -unsigned long ax; -unsigned long dx; -unsigned long bp; -}; - -static inline unsigned long fgraph_ret_regs_return_value (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->ax; -} - -static inline unsigned long fgraph_ret_regs_frame_pointer (struct fgraph_ret_regs *ret_regs) -{ -return ret_regs->bp; -} -#endif /* ifdef CONFIG_FUNCTION_GRAPH_TRACER */ -#endif -#endif /* _ASM_X86_FTRACE_H */ arch/x86/kernel/ftrace_32.S +9
-6@@ -23,6 +23,8 @@ SYM_FUNC_START(__fentry__) SYM_FUNC_END(__fentry__) EXPORT_SYMBOL(__fentry__) +#define FRAME_SIZE PT_OLDSS+4 + SYM_CODE_START(ftrace_caller) #ifdef CONFIG_FRAME_POINTER@@ -187,14 +189,15 @@ SYM_CODE_END(ftrace_graph_caller) .globl return_to_handler return_to_handler: - pushl $0 - pushl %edx - pushl %eax + subl $(FRAME_SIZE), %esp + movl $0, PT_EBP(%esp) + movl %edx, PT_EDX(%esp) + movl %eax, PT_EAX(%esp) movl %esp, %eax call ftrace_return_to_handler movl %eax, %ecx - popl %eax - popl %edx - addl $4, %esp # skip ebp + movl %eax, PT_EAX(%esp) + movl %edx, PT_EDX(%esp) + addl $(FRAME_SIZE), %esp JMP_NOSPEC ecx #endifarch/x86/kernel/ftrace_64.S +9
-8@@ -348,21 +348,22 @@ STACK_FRAME_NON_STANDARD_FP(__fentry__) SYM_CODE_START(return_to_handler) UNWIND_HINT_UNDEFINED ANNOTATE_NOENDBR - subq $24, %rsp - /* Save the return values */ - movq %rax, (%rsp) - movq %rdx, 8(%rsp) - movq %rbp, 16(%rsp) + /* Save ftrace_regs for function exit context */ + subq $(FRAME_SIZE), %rsp + + movq %rax, RAX(%rsp) + movq %rdx, RDX(%rsp) + movq %rbp, RBP(%rsp) movq %rsp, %rdi call ftrace_return_to_handler movq %rax, %rdi - movq 8(%rsp), %rdx - movq (%rsp), %rax + movq RDX(%rsp), %rdx + movq RAX(%rsp), %rax - addq $24, %rsp + addq $(FRAME_SIZE), %rsp /* * Jump back to the old return address. This cannot be JMP_NOSPEC rdi * since IBT would demand that contain ENDBR, which simply isn't so forinclude/linux/ftrace.h +11
-3@@ -43,9 +43,8 @@ struct dyn_ftrace;char *arch_ftrace_match_adjust (char *str,const char *search); -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -struct fgraph_ret_regs ; -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs); +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs);#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer);#endif @@ -134,6 +133,13 @@ extern int ftrace_enabled;* Also, architecture dependent fields can be used for internal process. * (e.g. orig_ax on x86_64) * +* Basically, ftrace_regs stores the registers related to the context. +* On function entry, registers for function parameters and hooking the +* function call are stored, and on function exit, registers for function +* return value and frame pointers are stored. +* +* And also, it dpends on the context that which registers are restored +* from the ftrace_regs. * On the function entry, those registers will be restored except for * the stack pointer, so that user can change the function parameters * and instruction pointer (e.g. live patching.) @@ -191,6 +197,8 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs) override_function_with_return(ftrace_get_regs(fregs))#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +#define ftrace_regs_get_frame_pointer (fregs) \ + frame_pointer(&(fregs)->regs)#endif typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,kernel/trace/Kconfig +2
-2@@ -31,7 +31,7 @@ config HAVE_FUNCTION_GRAPH_TRACER help See Documentation/trace/ftrace-design.rst -config HAVE_FUNCTION_GRAPH_RETVAL +config HAVE_FUNCTION_GRAPH_FREGS bool config HAVE_DYNAMIC_FTRACE@@ -232,7 +232,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_FREGS depends on FUNCTION_GRAPH_TRACER default n helpkernel/trace/fgraph.c +9
-12@@ -763,15 +763,12 @@ static struct notifier_block ftrace_suspend_notifier = { .notifier_call = ftrace_suspend_notifier_call, }; -/* fgraph_ret_regs is not defined without CONFIG_FUNCTION_GRAPH_RETVAL */ -struct fgraph_ret_regs ; -/* * 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, -unsigned long frame_pointer) +static inline unsigned long +__ftrace_return_to_handler (struct ftrace_regs *fregs,unsigned long frame_pointer) {struct ftrace_ret_stack *ret_stack;struct ftrace_graph_ret trace;@@ -791,7 +788,7 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs trace.rettime =trace_clock_local ();#ifdef CONFIG_FUNCTION_GRAPH_RETVAL - trace.retval =fgraph_ret_regs_return_value (ret_regs); + trace.retval =ftrace_regs_get_return_value (fregs);#endif bitmap =get_bitmap_bits (current, offset);@@ -826,14 +823,14 @@ static unsigned long __ftrace_return_to_handler(struct fgraph_ret_regs *ret_regs }/* -* After all architecures have selected HAVE_FUNCTION_GRAPH_RETVAL, we can -* leave only ftrace_return_to_handler(ret_regs). +* After all architecures have selected HAVE_FUNCTION_GRAPH_FREGS, we can +* leave only ftrace_return_to_handler(fregs). */ -#ifdef CONFIG_HAVE_FUNCTION_GRAPH_RETVAL -unsigned long ftrace_return_to_handler (struct fgraph_ret_regs *ret_regs) +#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FREGS +unsigned long ftrace_return_to_handler (struct ftrace_regs *fregs) { -return __ftrace_return_to_handler (ret_regs, -fgraph_ret_regs_frame_pointer (ret_regs)); +return __ftrace_return_to_handler (fregs, +ftrace_regs_get_frame_pointer (fregs)); }#else unsigned long ftrace_return_to_handler (unsigned long frame_pointer)
Masami Hiramatsu (Google)
maintainerPATCH v14 5/19 function_graph: Pass ftrace_regs to retfunc
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Pass ftrace_regs to the fgraph_ops::retfunc(). If ftrace_regs is not available, it passes a NULL instead. User callback function can access some registers (including return address) via this ftrace_regs.
---
Changes in v8:
- Pass ftrace_regs to retfunc, instead of adding retregfunc.
Changes in v6:
- update to use ftrace_regs_get_return_value() because of reordering patches.
Changes in v3:
- Update for new multiple fgraph.
- Save the return address to instruction pointer in ftrace_regs.
--- include/linux/ftrace.h | 3 ++- kernel/trace/fgraph.c | 16 +++++++++++----- kernel/trace/ftrace.c | 3 ++- kernel/trace/trace.h | 3 ++- kernel/trace/trace_functions_graph.c | 7 ++++--- kernel/trace/trace_irqsoff.c | 3 ++- kernel/trace/trace_sched_wakeup.c | 3 ++- kernel/trace/trace_selftest.c | 3 ++- 8 files changed, 27 insertions(+), 14 deletions(-)
include/linux/ftrace.h +2 -1
@@ -1069,7 +1069,8 @@ struct fgraph_ops;/* Type of the callback handlers for tracing function graph*/ typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *, -struct fgraph_ops *);/* return */ +struct fgraph_ops *, +struct ftrace_regs *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *,struct fgraph_ops *,struct ftrace_regs *);/* entry */
kernel/trace/fgraph.c +11 -5
@@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops, }/* ftrace_graph_return set to this to tell some archs to run function graph */ -static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops) +static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) { }
@@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, }static void ftrace_graph_ret_stub (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { }
@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe } trace.rettime =trace_clock_local (); +if (fregs) +ftrace_regs_set_instruction_pointer (fregs, ret); +#ifdef CONFIG_FUNCTION_GRAPH_RETVAL trace.retval =ftrace_regs_get_return_value (fregs);#endif
@@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe#ifdef CONFIG_HAVE_STATIC_CALL if (static_branch_likely (&fgraph_do_direct)) {if (test_bit (fgraph_direct_gops->idx, &bitmap)) -static_call (fgraph_retfunc)(&trace, fgraph_direct_gops); +static_call (fgraph_retfunc)(&trace, fgraph_direct_gops, fregs); } else#endif {
@@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointeif (gops == &fgraph_stub)continue ; - gops->retfunc (&trace, gops); + gops->retfunc (&trace, gops, fregs); } }
@@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)* Simply points to ftrace_stub, but with the proper protocol. * Defined by the linker script in linux/vmlinux.lds.h */ -void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);/* The callbacks that hook a function */ trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;
kernel/trace/ftrace.c +2 -1
@@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace, }static void profile_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;struct ftrace_profile_stat *stat;
kernel/trace/trace.h +2 -1
@@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);void trace_default_header (struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter); -void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops,struct ftrace_regs *fregs);
kernel/trace/trace_functions_graph.c +4 -3
@@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr, }void trace_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;
@@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace, }static void trace_graph_thresh_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {ftrace_graph_addr_finish (gops, trace);
@@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace, (trace->rettime - trace->calltime < tracing_thresh)) return; else -trace_graph_return (trace ,gops ); +trace_graph_return (trace ,gops ,fregs ); }static struct fgraph_ops funcgraph_ops = {
kernel/trace/trace_irqsoff.c +2 -1
@@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace, }static void irqsoff_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;
kernel/trace/trace_sched_wakeup.c +2 -1
@@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace, }static void wakeup_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;
kernel/trace/trace_selftest.c +2 -1
@@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace, }static __init void store_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;
Steven Rostedt
maintainerRe: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
Can I get an Acked-by from the AARCH64 maintainers for this patch?
Thanks!
-- Steve
[ Note this is modifies the return side ]
Quoted 205 lines · On Fri, 13 Sep 2024 00:09:02 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::retfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Passftrace_regsto retfunc, instead of adding retregfunc.
Changes in v6:
- update to useftrace_regs_get_return_value()because of reordering patches.
Changes in v3:
- Update for new multiple fgraph.
- Save the return address to instruction pointer inftrace_regs.--- include/linux/ftrace.h | 3 ++- kernel/trace/fgraph.c | 16 +++++++++++----- kernel/trace/ftrace.c | 3 ++- kernel/trace/trace.h | 3 ++- kernel/trace/trace_functions_graph.c | 7 ++++--- kernel/trace/trace_irqsoff.c | 3 ++- kernel/trace/trace_sched_wakeup.c | 3 ++- kernel/trace/trace_selftest.c | 3 ++- 8 files changed, 27 insertions(+), 14 deletions(-)include/linux/ftrace.h +2
-1@@ -1069,7 +1069,8 @@ struct fgraph_ops;/* Type of the callback handlers for tracing function graph*/ typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *, -struct fgraph_ops *);/* return */ +struct fgraph_ops *, +struct ftrace_regs *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *,struct fgraph_ops *,struct ftrace_regs *);/* entry */ kernel/trace/fgraph.c +11
-5@@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops, }/* ftrace_graph_return set to this to tell some archs to run function graph */ -static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops) +static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) { }@@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, }static void ftrace_graph_ret_stub (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { }@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe } trace.rettime =trace_clock_local (); +if (fregs) +ftrace_regs_set_instruction_pointer (fregs, ret); +#ifdef CONFIG_FUNCTION_GRAPH_RETVAL trace.retval =ftrace_regs_get_return_value (fregs);#endif @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe#ifdef CONFIG_HAVE_STATIC_CALL if (static_branch_likely (&fgraph_do_direct)) {if (test_bit (fgraph_direct_gops->idx, &bitmap)) -static_call (fgraph_retfunc)(&trace, fgraph_direct_gops); +static_call (fgraph_retfunc)(&trace, fgraph_direct_gops, fregs); } else#endif {@@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointeif (gops == &fgraph_stub)continue ; - gops->retfunc (&trace, gops); + gops->retfunc (&trace, gops, fregs); } }@@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)* Simply points to ftrace_stub, but with the proper protocol. * Defined by the linker script in linux/vmlinux.lds.h */ -void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);/* The callbacks that hook a function */ trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;kernel/trace/ftrace.c +2
-1@@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace, }static void profile_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;struct ftrace_profile_stat *stat;kernel/trace/trace.h +2
-1@@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);void trace_default_header (struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter); -void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops,struct ftrace_regs *fregs);kernel/trace/trace_functions_graph.c +4
-3@@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr, }void trace_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;@@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace, }static void trace_graph_thresh_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {ftrace_graph_addr_finish (gops, trace);@@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace, (trace->rettime - trace->calltime < tracing_thresh)) return; else -trace_graph_return (trace ,gops ); +trace_graph_return (trace ,gops ,fregs ); }static struct fgraph_ops funcgraph_ops = {kernel/trace/trace_irqsoff.c +2
-1@@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace, }static void irqsoff_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace, }static void wakeup_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +2
-1@@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace, }static __init void store_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;
Will Deacon
maintainerRe: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
Quoted 68 lines · Steven Rostedt
Quoted 65 lines · On Fri, 13 Sep 2024 00:09:02 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::retfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Passftrace_regsto retfunc, instead of adding retregfunc.
Changes in v6:
- update to useftrace_regs_get_return_value()because of reordering patches.
Changes in v3:
- Update for new multiple fgraph.
- Save the return address to instruction pointer inftrace_regs.--- include/linux/ftrace.h | 3 ++- kernel/trace/fgraph.c | 16 +++++++++++----- kernel/trace/ftrace.c | 3 ++- kernel/trace/trace.h | 3 ++- kernel/trace/trace_functions_graph.c | 7 ++++--- kernel/trace/trace_irqsoff.c | 3 ++- kernel/trace/trace_sched_wakeup.c | 3 ++- kernel/trace/trace_selftest.c | 3 ++- 8 files changed, 27 insertions(+), 14 deletions(-)include/linux/ftrace.h +2
-1@@ -1069,7 +1069,8 @@ struct fgraph_ops;/* Type of the callback handlers for tracing function graph*/ typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *, -struct fgraph_ops *);/* return */ +struct fgraph_ops *, +struct ftrace_regs *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *,struct fgraph_ops *,struct ftrace_regs *);/* entry */ kernel/trace/fgraph.c +4
-2@@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops, }/* ftrace_graph_return set to this to tell some archs to run function graph */ -static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops) +static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) { }@@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, }static void ftrace_graph_ret_stub (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { }
Quoted 6 lines
kernel/trace/fgraph.c __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe · line 793
Quoted 6 lines
kernel/trace/fgraph.c +2
-0@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe } trace.rettime =trace_clock_local (); +if (fregs) +ftrace_regs_set_instruction_pointer (fregs, ret);
Where does the instruction pointer get used after this? The arm64 'return_to_handler' function doesn't look at it when we return.
Will
Steven Rostedt
maintainerRe: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
Quoted 9 lines · On Tue, 17 Sep 2024 11:08:48 +0100 Will Deacon <[email protected]> wrote:
Quoted 6 lines
kernel/trace/fgraph.c __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe · line 793
Quoted 6 lines
kernel/trace/fgraph.c +2
-0@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe } trace.rettime =trace_clock_local (); +if (fregs) +ftrace_regs_set_instruction_pointer (fregs, ret);Where does the instruction pointer get used after this? The arm64 '
return_to_handler' function doesn't look at it when we return.
It's for the hooks to the return instruction. kretprobes will start using function graph tracer to hook to a return of a function (via fprobes), and the callbacks will need access to the return pointer. The callbacks get passed the ftrace_regs, and this is how they can see what the function is returning to. For example, BPF programs will need this.
So it's not needed for the infrastructure, only the callbacks that hook to it.
-- Steve
Masami Hiramatsu (Google)
maintainerRe: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
Quoted 6 lines · On Mon, 30 Sep 2024 15:03:02 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 3 lines · On Tue, 17 Sep 2024 11:08:48 +0100 Will Deacon <[email protected]> wrote:
Quoted 3 lines
Quoted 3 lines
(quoted diff) +0
-0@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe }
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
kernel/trace/fgraph.c __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe · line 793
Quoted 3 lines
kernel/trace/fgraph.c +2
-0trace.rettime =trace_clock_local (); +if (fregs) +ftrace_regs_set_instruction_pointer (fregs, ret);Where does the instruction pointer get used after this? The arm64 '
return_to_handler' function doesn't look at it when we return.It's for the hooks to the return instruction. kretprobes will start using
not kretprobes, but fprobe. kretprobes continue using rethook.
Quoted 7 lines
function graph tracer to hook to a return of a function (via fprobes), and the callbacks will need access to the return pointer. The callbacks get passed the
ftrace_regs, and this is how they can see what the function is returning to. For example, BPF programs will need this.So it's not needed for the infrastructure, only the callbacks that hook to it.
Yes, it will be used for showing where to return in the fprobe exit event. More specifically, in the fprobe_return()@kernel/trace/fprobe.c in PATCH 13/19, it is extracted from fregs.
+static void fprobe_return(struct ftrace_graph_ret *trace,
+ struct fgraph_ops *gops,
+ struct ftrace_regs *fregs)
+{
+ unsigned long *fgraph_data = NULL;
+ unsigned long ret_ip;
+ unsigned long val;
+ struct fprobe *fp;
+ int size, curr;
+ int size_words;
+
+ fgraph_data = (unsigned long *)fgraph_retrieve_data(gops->idx, &size);
+ if (WARN_ON_ONCE(!fgraph_data))
return;
+ size_words = SIZE_IN_LONG(size);
+ ret_ip = ftrace_regs_get_instruction_pointer(fregs);
+
Thank you,
Quoted 2 lines
-- Steve
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Steven Rostedt
maintainerRe: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the LOONGARCH maintainers for this patch?
Thanks!
-- Steve
[ Note this is modifies the return side ]
Quoted 205 lines · On Fri, 13 Sep 2024 00:09:02 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::retfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Passftrace_regsto retfunc, instead of adding retregfunc.
Changes in v6:
- update to useftrace_regs_get_return_value()because of reordering patches.
Changes in v3:
- Update for new multiple fgraph.
- Save the return address to instruction pointer inftrace_regs.--- include/linux/ftrace.h | 3 ++- kernel/trace/fgraph.c | 16 +++++++++++----- kernel/trace/ftrace.c | 3 ++- kernel/trace/trace.h | 3 ++- kernel/trace/trace_functions_graph.c | 7 ++++--- kernel/trace/trace_irqsoff.c | 3 ++- kernel/trace/trace_sched_wakeup.c | 3 ++- kernel/trace/trace_selftest.c | 3 ++- 8 files changed, 27 insertions(+), 14 deletions(-)include/linux/ftrace.h +2
-1@@ -1069,7 +1069,8 @@ struct fgraph_ops;/* Type of the callback handlers for tracing function graph*/ typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *, -struct fgraph_ops *);/* return */ +struct fgraph_ops *, +struct ftrace_regs *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *,struct fgraph_ops *,struct ftrace_regs *);/* entry */ kernel/trace/fgraph.c +11
-5@@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops, }/* ftrace_graph_return set to this to tell some archs to run function graph */ -static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops) +static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) { }@@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, }static void ftrace_graph_ret_stub (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { }@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe } trace.rettime =trace_clock_local (); +if (fregs) +ftrace_regs_set_instruction_pointer (fregs, ret); +#ifdef CONFIG_FUNCTION_GRAPH_RETVAL trace.retval =ftrace_regs_get_return_value (fregs);#endif @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe#ifdef CONFIG_HAVE_STATIC_CALL if (static_branch_likely (&fgraph_do_direct)) {if (test_bit (fgraph_direct_gops->idx, &bitmap)) -static_call (fgraph_retfunc)(&trace, fgraph_direct_gops); +static_call (fgraph_retfunc)(&trace, fgraph_direct_gops, fregs); } else#endif {@@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointeif (gops == &fgraph_stub)continue ; - gops->retfunc (&trace, gops); + gops->retfunc (&trace, gops, fregs); } }@@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)* Simply points to ftrace_stub, but with the proper protocol. * Defined by the linker script in linux/vmlinux.lds.h */ -void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);/* The callbacks that hook a function */ trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;kernel/trace/ftrace.c +2
-1@@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace, }static void profile_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;struct ftrace_profile_stat *stat;kernel/trace/trace.h +2
-1@@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);void trace_default_header (struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter); -void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops,struct ftrace_regs *fregs);kernel/trace/trace_functions_graph.c +4
-3@@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr, }void trace_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;@@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace, }static void trace_graph_thresh_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {ftrace_graph_addr_finish (gops, trace);@@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace, (trace->rettime - trace->calltime < tracing_thresh)) return; else -trace_graph_return (trace ,gops ); +trace_graph_return (trace ,gops ,fregs ); }static struct fgraph_ops funcgraph_ops = {kernel/trace/trace_irqsoff.c +2
-1@@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace, }static void irqsoff_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace, }static void wakeup_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +2
-1@@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace, }static __init void store_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;
Steven Rostedt
maintainerRe: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the POWERPC maintainers for this patch?
Thanks!
-- Steve
[ Note this is modifies the return side ]
Quoted 205 lines · On Fri, 13 Sep 2024 00:09:02 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::retfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Passftrace_regsto retfunc, instead of adding retregfunc.
Changes in v6:
- update to useftrace_regs_get_return_value()because of reordering patches.
Changes in v3:
- Update for new multiple fgraph.
- Save the return address to instruction pointer inftrace_regs.--- include/linux/ftrace.h | 3 ++- kernel/trace/fgraph.c | 16 +++++++++++----- kernel/trace/ftrace.c | 3 ++- kernel/trace/trace.h | 3 ++- kernel/trace/trace_functions_graph.c | 7 ++++--- kernel/trace/trace_irqsoff.c | 3 ++- kernel/trace/trace_sched_wakeup.c | 3 ++- kernel/trace/trace_selftest.c | 3 ++- 8 files changed, 27 insertions(+), 14 deletions(-)include/linux/ftrace.h +2
-1@@ -1069,7 +1069,8 @@ struct fgraph_ops;/* Type of the callback handlers for tracing function graph*/ typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *, -struct fgraph_ops *);/* return */ +struct fgraph_ops *, +struct ftrace_regs *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *,struct fgraph_ops *,struct ftrace_regs *);/* entry */ kernel/trace/fgraph.c +11
-5@@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops, }/* ftrace_graph_return set to this to tell some archs to run function graph */ -static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops) +static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) { }@@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, }static void ftrace_graph_ret_stub (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { }@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe } trace.rettime =trace_clock_local (); +if (fregs) +ftrace_regs_set_instruction_pointer (fregs, ret); +#ifdef CONFIG_FUNCTION_GRAPH_RETVAL trace.retval =ftrace_regs_get_return_value (fregs);#endif @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe#ifdef CONFIG_HAVE_STATIC_CALL if (static_branch_likely (&fgraph_do_direct)) {if (test_bit (fgraph_direct_gops->idx, &bitmap)) -static_call (fgraph_retfunc)(&trace, fgraph_direct_gops); +static_call (fgraph_retfunc)(&trace, fgraph_direct_gops, fregs); } else#endif {@@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointeif (gops == &fgraph_stub)continue ; - gops->retfunc (&trace, gops); + gops->retfunc (&trace, gops, fregs); } }@@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)* Simply points to ftrace_stub, but with the proper protocol. * Defined by the linker script in linux/vmlinux.lds.h */ -void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);/* The callbacks that hook a function */ trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;kernel/trace/ftrace.c +2
-1@@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace, }static void profile_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;struct ftrace_profile_stat *stat;kernel/trace/trace.h +2
-1@@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);void trace_default_header (struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter); -void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops,struct ftrace_regs *fregs);kernel/trace/trace_functions_graph.c +4
-3@@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr, }void trace_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;@@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace, }static void trace_graph_thresh_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {ftrace_graph_addr_finish (gops, trace);@@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace, (trace->rettime - trace->calltime < tracing_thresh)) return; else -trace_graph_return (trace ,gops ); +trace_graph_return (trace ,gops ,fregs ); }static struct fgraph_ops funcgraph_ops = {kernel/trace/trace_irqsoff.c +2
-1@@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace, }static void irqsoff_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace, }static void wakeup_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +2
-1@@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace, }static __init void store_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;
Steven Rostedt
maintainerRe: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the RISC-V maintainers for this patch?
Thanks!
-- Steve
[ Note this is modifies the return side ]
Quoted 205 lines · On Fri, 13 Sep 2024 00:09:02 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::retfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Passftrace_regsto retfunc, instead of adding retregfunc.
Changes in v6:
- update to useftrace_regs_get_return_value()because of reordering patches.
Changes in v3:
- Update for new multiple fgraph.
- Save the return address to instruction pointer inftrace_regs.--- include/linux/ftrace.h | 3 ++- kernel/trace/fgraph.c | 16 +++++++++++----- kernel/trace/ftrace.c | 3 ++- kernel/trace/trace.h | 3 ++- kernel/trace/trace_functions_graph.c | 7 ++++--- kernel/trace/trace_irqsoff.c | 3 ++- kernel/trace/trace_sched_wakeup.c | 3 ++- kernel/trace/trace_selftest.c | 3 ++- 8 files changed, 27 insertions(+), 14 deletions(-)include/linux/ftrace.h +2
-1@@ -1069,7 +1069,8 @@ struct fgraph_ops;/* Type of the callback handlers for tracing function graph*/ typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *, -struct fgraph_ops *);/* return */ +struct fgraph_ops *, +struct ftrace_regs *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *,struct fgraph_ops *,struct ftrace_regs *);/* entry */ kernel/trace/fgraph.c +11
-5@@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops, }/* ftrace_graph_return set to this to tell some archs to run function graph */ -static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops) +static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) { }@@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, }static void ftrace_graph_ret_stub (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { }@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe } trace.rettime =trace_clock_local (); +if (fregs) +ftrace_regs_set_instruction_pointer (fregs, ret); +#ifdef CONFIG_FUNCTION_GRAPH_RETVAL trace.retval =ftrace_regs_get_return_value (fregs);#endif @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe#ifdef CONFIG_HAVE_STATIC_CALL if (static_branch_likely (&fgraph_do_direct)) {if (test_bit (fgraph_direct_gops->idx, &bitmap)) -static_call (fgraph_retfunc)(&trace, fgraph_direct_gops); +static_call (fgraph_retfunc)(&trace, fgraph_direct_gops, fregs); } else#endif {@@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointeif (gops == &fgraph_stub)continue ; - gops->retfunc (&trace, gops); + gops->retfunc (&trace, gops, fregs); } }@@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)* Simply points to ftrace_stub, but with the proper protocol. * Defined by the linker script in linux/vmlinux.lds.h */ -void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);/* The callbacks that hook a function */ trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;kernel/trace/ftrace.c +2
-1@@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace, }static void profile_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;struct ftrace_profile_stat *stat;kernel/trace/trace.h +2
-1@@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);void trace_default_header (struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter); -void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops,struct ftrace_regs *fregs);kernel/trace/trace_functions_graph.c +4
-3@@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr, }void trace_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;@@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace, }static void trace_graph_thresh_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {ftrace_graph_addr_finish (gops, trace);@@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace, (trace->rettime - trace->calltime < tracing_thresh)) return; else -trace_graph_return (trace ,gops ); +trace_graph_return (trace ,gops ,fregs ); }static struct fgraph_ops funcgraph_ops = {kernel/trace/trace_irqsoff.c +2
-1@@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace, }static void irqsoff_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace, }static void wakeup_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +2
-1@@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace, }static __init void store_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;
Steven Rostedt
maintainerRe: [PATCH v14 05/19] function_graph: Pass ftrace_regs to retfunc
In reply to Masami Hiramatsu (Google)Can I get an Acked-by from the X86 maintainers for this patch?
Thanks!
-- Steve
[ Note this is modifies the return side ]
Quoted 205 lines · On Fri, 13 Sep 2024 00:09:02 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Pass
ftrace_regsto thefgraph_ops::retfunc(). Ifftrace_regsis not available, it passes a NULL instead. User callback function can access some registers (including return address) via thisftrace_regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>---
Changes in v8:
- Passftrace_regsto retfunc, instead of adding retregfunc.
Changes in v6:
- update to useftrace_regs_get_return_value()because of reordering patches.
Changes in v3:
- Update for new multiple fgraph.
- Save the return address to instruction pointer inftrace_regs.--- include/linux/ftrace.h | 3 ++- kernel/trace/fgraph.c | 16 +++++++++++----- kernel/trace/ftrace.c | 3 ++- kernel/trace/trace.h | 3 ++- kernel/trace/trace_functions_graph.c | 7 ++++--- kernel/trace/trace_irqsoff.c | 3 ++- kernel/trace/trace_sched_wakeup.c | 3 ++- kernel/trace/trace_selftest.c | 3 ++- 8 files changed, 27 insertions(+), 14 deletions(-)include/linux/ftrace.h +2
-1@@ -1069,7 +1069,8 @@ struct fgraph_ops;/* Type of the callback handlers for tracing function graph*/ typedef void (*trace_func_graph_ret_t )(struct ftrace_graph_ret *, -struct fgraph_ops *);/* return */ +struct fgraph_ops *, +struct ftrace_regs *);/* return */ typedef int (*trace_func_graph_ent_t )(struct ftrace_graph_ent *,struct fgraph_ops *,struct ftrace_regs *);/* entry */ kernel/trace/fgraph.c +11
-5@@ -297,7 +297,8 @@ static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops, }/* ftrace_graph_return set to this to tell some archs to run function graph */ -static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops) +static void return_run (struct ftrace_graph_ret *trace,struct fgraph_ops *ops, +struct ftrace_regs *fregs) { }@@ -491,7 +492,8 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, }static void ftrace_graph_ret_stub (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { }@@ -787,6 +789,9 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe } trace.rettime =trace_clock_local (); +if (fregs) +ftrace_regs_set_instruction_pointer (fregs, ret); +#ifdef CONFIG_FUNCTION_GRAPH_RETVAL trace.retval =ftrace_regs_get_return_value (fregs);#endif @@ -796,7 +801,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointe#ifdef CONFIG_HAVE_STATIC_CALL if (static_branch_likely (&fgraph_do_direct)) {if (test_bit (fgraph_direct_gops->idx, &bitmap)) -static_call (fgraph_retfunc)(&trace, fgraph_direct_gops); +static_call (fgraph_retfunc)(&trace, fgraph_direct_gops, fregs); } else#endif {@@ -806,7 +811,7 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointeif (gops == &fgraph_stub)continue ; - gops->retfunc (&trace, gops); + gops->retfunc (&trace, gops, fregs); } }@@ -956,7 +961,8 @@ void ftrace_graph_sleep_time_control(bool enable)* Simply points to ftrace_stub, but with the proper protocol. * Defined by the linker script in linux/vmlinux.lds.h */ -void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void ftrace_stub_graph (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);/* The callbacks that hook a function */ trace_func_graph_ret_t ftrace_graph_return = ftrace_stub_graph;kernel/trace/ftrace.c +2
-1@@ -840,7 +840,8 @@ static int profile_graph_entry(struct ftrace_graph_ent *trace, }static void profile_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct ftrace_ret_stack *ret_stack;struct ftrace_profile_stat *stat;kernel/trace/trace.h +2
-1@@ -682,7 +682,8 @@ void trace_latency_header(struct seq_file *m);void trace_default_header (struct seq_file *m);void print_trace_header (struct seq_file *m,struct trace_iterator *iter); -void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops); +void trace_graph_return (struct ftrace_graph_ret *trace,struct fgraph_ops *gops, +struct ftrace_regs *fregs);int trace_graph_entry (struct ftrace_graph_ent *trace,struct fgraph_ops *gops,struct ftrace_regs *fregs);kernel/trace/trace_functions_graph.c +4
-3@@ -240,7 +240,7 @@ void __trace_graph_return(struct trace_array *tr, }void trace_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops,struct ftrace_regs *fregs) {unsigned long *task_var =fgraph_get_task_var (gops);struct trace_array *tr = gops->private;@@ -270,7 +270,8 @@ void trace_graph_return(struct ftrace_graph_ret *trace, }static void trace_graph_thresh_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {ftrace_graph_addr_finish (gops, trace);@@ -283,7 +284,7 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace, (trace->rettime - trace->calltime < tracing_thresh)) return; else -trace_graph_return (trace ,gops ); +trace_graph_return (trace ,gops ,fregs ); }static struct fgraph_ops funcgraph_ops = {kernel/trace/trace_irqsoff.c +2
-1@@ -208,7 +208,8 @@ static int irqsoff_graph_entry(struct ftrace_graph_ent *trace, }static void irqsoff_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = irqsoff_trace;struct trace_array_cpu *data;kernel/trace/trace_sched_wakeup.c +2
-1@@ -144,7 +144,8 @@ static int wakeup_graph_entry(struct ftrace_graph_ent *trace, }static void wakeup_graph_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) {struct trace_array *tr = wakeup_trace;struct trace_array_cpu *data;kernel/trace/trace_selftest.c +2
-1@@ -807,7 +807,8 @@ static __init int store_entry(struct ftrace_graph_ent *trace, }static __init void store_return (struct ftrace_graph_ret *trace, -struct fgraph_ops *gops) +struct fgraph_ops *gops, +struct ftrace_regs *fregs) { struct fgraph_fixture *fixture = container_of(gops, struct fgraph_fixture, gops);const char *type = fixture->store_type_name;
Masami Hiramatsu (Google)
maintainerPATCH v14 6/19 fprobe: Use ftrace_regs in fprobe entry handler
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
This allows fprobes to be available with CONFIG_DYNAMIC_FTRACE_WITH_ARGS instead of CONFIG_DYNAMIC_FTRACE_WITH_REGS, then we can enable fprobe on arm64.
---
Changes in v6:
- Keep using SAVE_REGS flag to avoid breaking bpf kprobe-multi test.
--- include/linux/fprobe.h | 2 +- kernel/trace/Kconfig | 3 ++- kernel/trace/bpf_trace.c | 10 +++++++--- kernel/trace/fprobe.c | 3 ++- kernel/trace/trace_fprobe.c | 6 +++++- lib/test_fprobe.c | 4 ++-- samples/fprobe/fprobe_example.c | 2 +- 7 files changed, 20 insertions(+), 10 deletions(-)
include/linux/fprobe.h +1 -1
@@ -10,7 +10,7 @@struct fprobe ;typedef int (*fprobe_entry_cb )(struct fprobe *fp,unsigned long entry_ip, -unsigned long ret_ip,struct pt_regs *regs, +unsigned long ret_ip,struct ftrace_regs *regs,void *entry_data); typedef void (*fprobe_exit_cb)(struct fprobe *fp, unsigned long entry_ip,
kernel/trace/Kconfig +2 -1
@@ -287,7 +287,7 @@ config DYNAMIC_FTRACE_WITH_ARGS config FPROBE bool "Kernel Function Probe (fprobe)" depends on FUNCTION_TRACER - depends on DYNAMIC_FTRACE_WITH_REGS + depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS depends on HAVE_RETHOOK select RETHOOK default n
@@ -672,6 +672,7 @@ config FPROBE_EVENTS select TRACING select PROBE_EVENTS select DYNAMIC_EVENTS + depends on DYNAMIC_FTRACE_WITH_REGS default y help This allows user to add tracing events on the function entry and
kernel/trace/bpf_trace.c +7 -3
@@ -2605,7 +2605,7 @@ struct bpf_session_run_ctx {void *data; }; -#ifdef CONFIG_FPROBE +#if defined(CONFIG_FPROBE) && defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS) struct bpf_kprobe_multi_link { struct bpf_link link; struct fprobe fp;
@@ -2857,12 +2857,16 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,static int kprobe_multi_link_handler (struct fprobe *fp,unsigned long fentry_ip, -unsigned long ret_ip,struct pt_regs *regs, +unsigned long ret_ip,struct ftrace_regs *fregs,void *data) { +struct pt_regs *regs =ftrace_get_regs (fregs);struct bpf_kprobe_multi_link *link;int err; +if (!regs) +return 0 ; + link = container_of(fp, struct bpf_kprobe_multi_link, fp); err =kprobe_multi_link_prog_run (link,get_entry_ip (fentry_ip), regs, false, data);return is_kprobe_session (link->link.prog) ? err :0 ;
@@ -3137,7 +3141,7 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prkvfree (cookies); return err; } -#else /* !CONFIG_FPROBE */ +#else /* !CONFIG_FPROBE || !CONFIG_DYNAMIC_FTRACE_WITH_REGS */ int bpf_kprobe_multi_link_attach (const union bpf_attr *attr,struct bpf_prog *prog) {return -EOPNOTSUPP ;
kernel/trace/fprobe.c +2 -1
@@ -46,7 +46,7 @@ static inline void __fprobe_handler(unsigned long ip, unsigned long parent_ip, }if (fp->entry_handler) - ret = fp->entry_handler (fp, ip, parent_ip,ftrace_get_regs (fregs), entry_data); + ret = fp->entry_handler (fp, ip, parent_ip, fregs, entry_data);/* If entry_handler returns !0, nmissed is not counted. */ if (rh) {
@@ -182,6 +182,7 @@ static void fprobe_init(struct fprobe *fp) fp->ops.func = fprobe_kprobe_handler; else fp->ops.func = fprobe_handler; + fp->ops.flags |=FTRACE_OPS_FL_SAVE_REGS ; }
kernel/trace/trace_fprobe.c +5 -1
@@ -338,12 +338,16 @@ NOKPROBE_SYMBOL(fexit_perf_func);#endif /* CONFIG_PERF_EVENTS */ static int fentry_dispatcher (struct fprobe *fp,unsigned long entry_ip, -unsigned long ret_ip,struct pt_regs *regs, +unsigned long ret_ip,struct ftrace_regs *fregs,void *entry_data) { struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp); +struct pt_regs *regs =ftrace_get_regs (fregs);int ret =0 ; +if (!regs) +return 0 ; +if (trace_probe_test_flag (&tf->tp,TP_FLAG_TRACE ))fentry_trace_func (tf, entry_ip, regs);#ifdef CONFIG_PERF_EVENTS
lib/test_fprobe.c +2 -2
@@ -40,7 +40,7 @@ static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32)) static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,unsigned long ret_ip, -struct pt_regs *regs,void *data) +struct ftrace_regs *fregs,void *data) {KUNIT_EXPECT_FALSE (current_test,preemptible ());/* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */
@@ -81,7 +81,7 @@ static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,unsigned long ret_ip, -struct pt_regs *regs,void *data) +struct ftrace_regs *fregs,void *data) {KUNIT_EXPECT_FALSE (current_test,preemptible ());return 0 ;
samples/fprobe/fprobe_example.c +1 -1
@@ -50,7 +50,7 @@ static void show_backtrace(void)static int sample_entry_handler (struct fprobe *fp,unsigned long ip,unsigned long ret_ip, -struct pt_regs *regs,void *data) +struct ftrace_regs *fregs,void *data) {if (use_trace)/*
Masami Hiramatsu (Google)
maintainerPATCH v14 7/19 fprobe: Use ftrace_regs in fprobe exit handler
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Change the fprobe exit handler to use ftrace_regs structure instead of pt_regs. This also introduce HAVE_PT_REGS_TO_FTRACE_REGS_CAST which means the ftrace_regs's memory layout is equal to the pt_regs so that those are able to cast. Fprobe introduces a new dependency with that.
---
Changes in v3:
- Use ftrace_regs_get_return_value()
Changes from previous series: NOTHING, just forward ported.
--- arch/loongarch/Kconfig | 1 + arch/s390/Kconfig | 1 + arch/x86/Kconfig | 1 + include/linux/fprobe.h | 2 +- include/linux/ftrace.h | 6 ++++++ kernel/trace/Kconfig | 8 ++++++++ kernel/trace/bpf_trace.c | 6 +++++- kernel/trace/fprobe.c | 3 ++- kernel/trace/trace_fprobe.c | 6 +++++- lib/test_fprobe.c | 6 +++--- samples/fprobe/fprobe_example.c | 2 +- 11 files changed, 34 insertions(+), 8 deletions(-)
arch/loongarch/Kconfig +1 -0
@@ -122,6 +122,7 @@ config LOONGARCH select HAVE_DMA_CONTIGUOUS select HAVE_DYNAMIC_FTRACE select HAVE_DYNAMIC_FTRACE_WITH_ARGS + select HAVE_PT_REGS_TO_FTRACE_REGS_CAST select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS select HAVE_DYNAMIC_FTRACE_WITH_REGS select HAVE_EBPF_JIT
arch/s390/Kconfig +1 -0
@@ -175,6 +175,7 @@ config S390 select HAVE_DMA_CONTIGUOUS select HAVE_DYNAMIC_FTRACE select HAVE_DYNAMIC_FTRACE_WITH_ARGS + select HAVE_PT_REGS_TO_FTRACE_REGS_CAST select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS select HAVE_DYNAMIC_FTRACE_WITH_REGS select HAVE_EBPF_JIT if HAVE_MARCH_Z196_FEATURES
arch/x86/Kconfig +1 -0
@@ -218,6 +218,7 @@ config X86 select HAVE_DYNAMIC_FTRACE select HAVE_DYNAMIC_FTRACE_WITH_REGS select HAVE_DYNAMIC_FTRACE_WITH_ARGS if X86_64 + select HAVE_PT_REGS_TO_FTRACE_REGS_CAST if X86_64 select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS select HAVE_SAMPLE_FTRACE_DIRECT if X86_64 select HAVE_SAMPLE_FTRACE_DIRECT_MULTI if X86_64
include/linux/fprobe.h +1 -1
@@ -14,7 +14,7 @@ typedef int (*fprobe_entry_cb)(struct fprobe *fp, unsigned long entry_ip, void *entry_data);typedef void (*fprobe_exit_cb )(struct fprobe *fp,unsigned long entry_ip, -unsigned long ret_ip,struct pt_regs *regs, +unsigned long ret_ip,struct ftrace_regs *regs,void *entry_data);/**
include/linux/ftrace.h +6 -0
@@ -162,6 +162,12 @@ struct ftrace_regs {#define ftrace_regs_set_instruction_pointer (fregs, ip) do { } while (0)#endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ +#ifdef CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST + +static_assert (sizeof (struct pt_regs ) ==sizeof (struct ftrace_regs )); + +#endif /* CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST */ + static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs) {if (!fregs)
kernel/trace/Kconfig +8 -0
@@ -57,6 +57,13 @@ config HAVE_DYNAMIC_FTRACE_WITH_ARGS This allows for use of ftrace_regs_get_argument() and ftrace_regs_get_stack_pointer(). +config HAVE_PT_REGS_TO_FTRACE_REGS_CAST + bool + help + If this is set, the memory layout of the ftrace_regs data structure + is the same as the pt_regs. So the pt_regs is possible to be casted + to ftrace_regs. + config HAVE_DYNAMIC_FTRACE_NO_PATCHABLE bool help
@@ -288,6 +295,7 @@ config FPROBE bool "Kernel Function Probe (fprobe)" depends on FUNCTION_TRACER depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS + depends on HAVE_PT_REGS_TO_FTRACE_REGS_CAST || !HAVE_DYNAMIC_FTRACE_WITH_ARGS depends on HAVE_RETHOOK select RETHOOK default n
kernel/trace/bpf_trace.c +5 -1
@@ -2874,10 +2874,14 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip,static void kprobe_multi_link_exit_handler (struct fprobe *fp,unsigned long fentry_ip, -unsigned long ret_ip,struct pt_regs *regs, +unsigned long ret_ip,struct ftrace_regs *fregs,void *data) {struct bpf_kprobe_multi_link *link; +struct pt_regs *regs =ftrace_get_regs (fregs); + +if (!regs) +return ; link = container_of(fp, struct bpf_kprobe_multi_link, fp);kprobe_multi_link_prog_run (link,get_entry_ip (fentry_ip), regs, true, data);
kernel/trace/fprobe.c +2 -1
@@ -124,6 +124,7 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data, {struct fprobe *fp = (struct fprobe *)data;struct fprobe_rethook_node *fpr; +struct ftrace_regs *fregs = (struct ftrace_regs *)regs;int bit;if (!fp ||fprobe_disabled (fp))
@@ -141,7 +142,7 @@ static void fprobe_exit_handler(struct rethook_node *rh, void *data,return ; } - fp->exit_handler (fp, fpr->entry_ip, ret_ip, regs, + fp->exit_handler (fp, fpr->entry_ip, ret_ip, fregs, fp->entry_data_size ? (void *)fpr->data :NULL );ftrace_test_recursion_unlock (bit); }
kernel/trace/trace_fprobe.c +5 -1
@@ -359,10 +359,14 @@ static int fentry_dispatcher(struct fprobe *fp, unsigned long entry_ip,NOKPROBE_SYMBOL (fentry_dispatcher);static void fexit_dispatcher (struct fprobe *fp,unsigned long entry_ip, -unsigned long ret_ip,struct pt_regs *regs, +unsigned long ret_ip,struct ftrace_regs *fregs,void *entry_data) { struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp); +struct pt_regs *regs =ftrace_get_regs (fregs); + +if (!regs) +return ;if (trace_probe_test_flag (&tf->tp,TP_FLAG_TRACE ))fexit_trace_func (tf, entry_ip, ret_ip, regs, entry_data);
lib/test_fprobe.c +3 -3
@@ -59,9 +59,9 @@ static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip, static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,unsigned long ret_ip, -struct pt_regs *regs,void *data) +struct ftrace_regs *fregs,void *data) { -unsigned long ret =regs_return_value (regs); +unsigned long ret =ftrace_regs_get_return_value (fregs);KUNIT_EXPECT_FALSE (current_test,preemptible ());if (ip != target_ip) {
@@ -89,7 +89,7 @@ static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip, static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip,unsigned long ret_ip, -struct pt_regs *regs,void *data) +struct ftrace_regs *fregs,void *data) {KUNIT_EXPECT_FALSE (current_test,preemptible ());KUNIT_EXPECT_EQ (current_test, ip, target_nest_ip);
samples/fprobe/fprobe_example.c +1 -1
@@ -67,7 +67,7 @@ static int sample_entry_handler(struct fprobe *fp, unsigned long ip, }static void sample_exit_handler (struct fprobe *fp,unsigned long ip, -unsigned long ret_ip,struct pt_regs *regs, +unsigned long ret_ip,struct ftrace_regs *regs,void *data) {unsigned long rip = ret_ip;
Masami Hiramatsu (Google)
maintainerPATCH v14 8/19 tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Add ftrace_partial_regs() which converts the ftrace_regs to pt_regs. This is for the eBPF which needs this to keep the same pt_regs interface to access registers.
Thus when replacing the pt_regs with ftrace_regs in fprobes (which is used by kprobe_multi eBPF event), this will be used.
If the architecture defines its own ftrace_regs, this copies partial registers to pt_regs and returns it. If not, ftrace_regs is the same as pt_regs and ftrace_partial_regs() will return ftrace_regs::regs.
---
Changes in v14:
- Add riscv change.
Changes in v8:
- Add the reason why this required in changelog.
Changes from previous series: NOTHING, just forward ported.
--- arch/arm64/include/asm/ftrace.h | 11 +++++++++++ arch/riscv/include/asm/ftrace.h | 12 ++++++++++++ include/linux/ftrace.h | 17 +++++++++++++++++ 3 files changed, 40 insertions(+)
arch/arm64/include/asm/ftrace.h +11 -0
@@ -132,6 +132,17 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)return fregs->fp; } +static __always_inline struct pt_regs * +ftrace_partial_regs (const struct ftrace_regs *fregs,struct pt_regs *regs) +{ +memcpy (regs->regs, fregs->regs,sizeof (u64) *9 ); + regs->sp = fregs->sp; + regs->pc = fregs->pc; + regs->regs[29 ] = fregs->fp; + regs->regs[30 ] = fregs->lr; +return regs; +} +int ftrace_regs_query_register_offset (const char *name);int ftrace_init_nop (struct module *mod,struct dyn_ftrace *rec);
arch/riscv/include/asm/ftrace.h +12 -0
@@ -193,6 +193,18 @@ static __always_inline void ftrace_override_function_with_return(struct ftrace_r fregs->epc = fregs->ra; } +static __always_inline struct pt_regs * +ftrace_partial_regs (const struct ftrace_regs *fregs,struct pt_regs *regs) +{ +memcpy (®s->a0, fregs->args,sizeof (u64) *8 ); + regs->epc = fregs->epc; + regs->ra = fregs->ra; + regs->sp = fregs->sp; + regs->s0 = fregs->s0; + regs->t1 = fregs->t1; +return regs; +} +int ftrace_regs_query_register_offset (const char *name); void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
include/linux/ftrace.h +17 -0
@@ -176,6 +176,23 @@ static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregsreturn arch_ftrace_get_regs (fregs); } +#if !defined(CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS ) || \ + defined(CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST ) + +static __always_inline struct pt_regs * +ftrace_partial_regs (struct ftrace_regs *fregs,struct pt_regs *regs) +{ +/* +* If CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST=y, ftrace_regs memory +* layout is the same as pt_regs. So always returns that address. +* Since arch_ftrace_get_regs() will check some members and may return +* NULL, we can not use it. +*/ +return &fregs->regs; +} + +#endif /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS || CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST */ +/* * When true, the ftrace_regs_{get,set}_*() functions may be used on fregs. * Note: this can be true even when ftrace_get_regs() cannot provide a pt_regs.
Steven Rostedt
maintainerRe: [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
Can I get an Acked-by from the AARCH64 maintainers for this patch?
Thanks!
-- Steve
Quoted 99 lines · On Fri, 13 Sep 2024 00:09:35 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Add
ftrace_partial_regs()which converts theftrace_regstopt_regs. This is for the eBPF which needs this to keep the samept_regsinterface to access registers.
Thus when replacing thept_regswithftrace_regsin fprobes (which is used bykprobe_multieBPF event), this will be used.If the architecture defines its own
ftrace_regs, this copies partial registers topt_regsand returns it. If not,ftrace_regsis the same aspt_regsandftrace_partial_regs()will returnftrace_regs::regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>Acked-by Florent Revest <[email protected]>---
Changes in v14:
- Add riscv change.
Changes in v8:
- Add the reason why this required in changelog.
Changes from previous series: NOTHING, just forward ported.--- arch/arm64/include/asm/ftrace.h | 11 +++++++++++ arch/riscv/include/asm/ftrace.h | 12 ++++++++++++ include/linux/ftrace.h | 17 +++++++++++++++++ 3 files changed, 40 insertions(+)arch/arm64/include/asm/ftrace.h +11
-0@@ -132,6 +132,17 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)return fregs->fp; } +static __always_inline struct pt_regs * +ftrace_partial_regs (const struct ftrace_regs *fregs,struct pt_regs *regs) +{ +memcpy (regs->regs, fregs->regs,sizeof (u64) *9 ); + regs->sp = fregs->sp; + regs->pc = fregs->pc; + regs->regs[29 ] = fregs->fp; + regs->regs[30 ] = fregs->lr; +return regs; +} +int ftrace_regs_query_register_offset (const char *name);int ftrace_init_nop (struct module *mod,struct dyn_ftrace *rec);arch/riscv/include/asm/ftrace.h +12
-0@@ -193,6 +193,18 @@ static __always_inline void ftrace_override_function_with_return(struct ftrace_r fregs->epc = fregs->ra; } +static __always_inline struct pt_regs * +ftrace_partial_regs (const struct ftrace_regs *fregs,struct pt_regs *regs) +{ +memcpy (®s->a0, fregs->args,sizeof (u64) *8 ); + regs->epc = fregs->epc; + regs->ra = fregs->ra; + regs->sp = fregs->sp; + regs->s0 = fregs->s0; + regs->t1 = fregs->t1; +return regs; +} +int ftrace_regs_query_register_offset (const char *name); void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,include/linux/ftrace.h +17
-0@@ -176,6 +176,23 @@ static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregsreturn arch_ftrace_get_regs (fregs); } +#if !defined(CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS ) || \ + defined(CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST ) + +static __always_inline struct pt_regs * +ftrace_partial_regs (struct ftrace_regs *fregs,struct pt_regs *regs) +{ +/* +* If CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST=y, ftrace_regs memory +* layout is the same as pt_regs. So always returns that address. +* Since arch_ftrace_get_regs() will check some members and may return +* NULL, we can not use it. +*/ +return &fregs->regs; +} + +#endif /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS || CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST */ +/* * When true, the ftrace_regs_{get,set}_*() functions may be used on fregs. * Note: this can be true even when ftrace_get_regs() cannot provide a pt_regs.
Re: [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
Quoted 37 lines · Steven Rostedt
Quoted 34 lines · On Fri, 13 Sep 2024 00:09:35 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Add
ftrace_partial_regs()which converts theftrace_regstopt_regs. This is for the eBPF which needs this to keep the samept_regsinterface to access registers.
Thus when replacing thept_regswithftrace_regsin fprobes (which is used bykprobe_multieBPF event), this will be used.If the architecture defines its own
ftrace_regs, this copies partial registers topt_regsand returns it. If not,ftrace_regsis the same aspt_regsandftrace_partial_regs()will returnftrace_regs::regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>Acked-by Florent Revest <[email protected]>---
Changes in v14:
- Add riscv change.
Changes in v8:
- Add the reason why this required in changelog.
Changes from previous series: NOTHING, just forward ported.--- arch/arm64/include/asm/ftrace.h | 11 +++++++++++ arch/riscv/include/asm/ftrace.h | 12 ++++++++++++ include/linux/ftrace.h | 17 +++++++++++++++++ 3 files changed, 40 insertions(+)arch/arm64/include/asm/ftrace.h +0
-0@@ -132,6 +132,17 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)return fregs->fp; }
Quoted 10 lines
arch/arm64/include/asm/ftrace.h ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs) · line 144
Quoted 10 lines
arch/arm64/include/asm/ftrace.h +10
-0+static __always_inline struct pt_regs * +ftrace_partial_regs (const struct ftrace_regs *fregs,struct pt_regs *regs) +{ +memcpy (regs->regs, fregs->regs,sizeof (u64) *9 ); + regs->sp = fregs->sp; + regs->pc = fregs->pc; + regs->regs[29 ] = fregs->fp; + regs->regs[30 ] = fregs->lr; +return regs; +}
Ah, I guess this is where we pick up the lr that was set in patch 5.
Will
Masami Hiramatsu (Google)
maintainerRe: [PATCH v14 08/19] tracing: Add ftrace_partial_regs() for converting ftrace_regs to pt_regs
Quoted 38 lines · On Tue, 17 Sep 2024 11:14:54 +0100 Will Deacon <[email protected]> wrote:
Quoted 37 lines · Steven Rostedt
Quoted 34 lines · On Fri, 13 Sep 2024 00:09:35 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Add
ftrace_partial_regs()which converts theftrace_regstopt_regs. This is for the eBPF which needs this to keep the samept_regsinterface to access registers.
Thus when replacing thept_regswithftrace_regsin fprobes (which is used bykprobe_multieBPF event), this will be used.If the architecture defines its own
ftrace_regs, this copies partial registers topt_regsand returns it. If not,ftrace_regsis the same aspt_regsandftrace_partial_regs()will returnftrace_regs::regs.Signed-off-by Masami Hiramatsu (Google) <[email protected]>Acked-by Florent Revest <[email protected]>---
Changes in v14:
- Add riscv change.
Changes in v8:
- Add the reason why this required in changelog.
Changes from previous series: NOTHING, just forward ported.--- arch/arm64/include/asm/ftrace.h | 11 +++++++++++ arch/riscv/include/asm/ftrace.h | 12 ++++++++++++ include/linux/ftrace.h | 17 +++++++++++++++++ 3 files changed, 40 insertions(+)arch/arm64/include/asm/ftrace.h +0
-0@@ -132,6 +132,17 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)return fregs->fp; }
Quoted 12 lines
Quoted 10 lines
arch/arm64/include/asm/ftrace.h ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs) · line 144
Quoted 10 lines
arch/arm64/include/asm/ftrace.h +10
-0+static __always_inline struct pt_regs * +ftrace_partial_regs (const struct ftrace_regs *fregs,struct pt_regs *regs) +{ +memcpy (regs->regs, fregs->regs,sizeof (u64) *9 ); + regs->sp = fregs->sp; + regs->pc = fregs->pc; + regs->regs[29 ] = fregs->fp; + regs->regs[30 ] = fregs->lr; +return regs; +}Ah, I guess this is where we pick up the lr that was set in patch 5.
Yeah, this one also copies the fregs->pc to regs->pc, mainly for bpf.
Quoted 2 lines
Acked-by Will Deacon <[email protected]>
Thank you!
Quoted 2 lines
Will
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Masami Hiramatsu (Google)
maintainerPATCH v14 9/19 tracing: Add ftrace_fill_perf_regs() for perf event
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Add ftrace_fill_perf_regs() which should be compatible with the perf_fetch_caller_regs(). In other words, the pt_regs returned from the ftrace_fill_perf_regs() must satisfy 'user_mode(regs) == false' and can be used for stack tracing.
---
Changes from previous series: NOTHING, just forward ported.
--- arch/arm64/include/asm/ftrace.h | 7 +++++++ arch/powerpc/include/asm/ftrace.h | 7 +++++++ arch/s390/include/asm/ftrace.h | 5 +++++ arch/x86/include/asm/ftrace.h | 7 +++++++ include/linux/ftrace.h | 31 +++++++++++++++++++++++++++++++ 5 files changed, 57 insertions(+)
arch/arm64/include/asm/ftrace.h +7 -0
@@ -143,6 +143,13 @@ ftrace_partial_regs(const struct ftrace_regs *fregs, struct pt_regs *regs)return regs; } +#define arch_ftrace_fill_perf_regs (fregs, _regs) do { \ + (_regs)->pc = (fregs)->pc; \ + (_regs)->regs[29] = (fregs)->fp; \ + (_regs)->sp = (fregs)->sp; \ + (_regs)->pstate = PSR_MODE_EL1h; \ + } while (0) +int ftrace_regs_query_register_offset (const char *name);int ftrace_init_nop (struct module *mod,struct dyn_ftrace *rec);
arch/powerpc/include/asm/ftrace.h +7 -0
@@ -42,6 +42,13 @@ static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *return fregs->regs.msr ? &fregs->regs :NULL ; } +#define arch_ftrace_fill_perf_regs (fregs, _regs) do { \ + (_regs)->result = 0; \ + (_regs)->nip = (fregs)->regs.nip; \ + (_regs)->gpr[1] = (fregs)->regs.gpr[1]; \ + asm volatile("mfmsr %0" : "=r" ((_regs)->msr)); \ + } while (0) + static __always_inline voidftrace_regs_set_instruction_pointer (struct ftrace_regs *fregs,unsigned long ip)
arch/s390/include/asm/ftrace.h +5 -0
@@ -84,6 +84,11 @@ ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)return sp[0 ];/* return backchain */ } +#define arch_ftrace_fill_perf_regs (fregs, _regs) do { \ + (_regs)->psw.addr = (fregs)->regs.psw.addr; \ + (_regs)->gprs[15] = (fregs)->regs.gprs[15]; \ + } while (0) +#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS /* * When an ftrace registered caller is tracing a function that is
arch/x86/include/asm/ftrace.h +7 -0
@@ -46,6 +46,13 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)return &fregs->regs; } +#define arch_ftrace_fill_perf_regs (fregs, _regs) do { \ + (_regs)->ip = (fregs)->regs.ip; \ + (_regs)->sp = (fregs)->regs.sp; \ + (_regs)->cs = __KERNEL_CS; \ + (_regs)->flags = 0; \ + } while (0) +#define ftrace_regs_set_instruction_pointer (fregs, _ip) \ do { (fregs)->regs.ip = (_ip); } while (0)
include/linux/ftrace.h +31 -0
@@ -193,6 +193,37 @@ ftrace_partial_regs(struct ftrace_regs *fregs, struct pt_regs *regs)#endif /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS || CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST */ +#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS + +/* +* Please define arch dependent pt_regs which compatible to the +* perf_arch_fetch_caller_regs() but based on ftrace_regs. +* This requires +* - user_mode(_regs) returns false (always kernel mode). +* - able to use the _regs for stack trace. +*/ +#ifndef arch_ftrace_fill_perf_regs +/* As same as perf_arch_fetch_caller_regs(), do nothing by default */ +#define arch_ftrace_fill_perf_regs (fregs, _regs) do {} while (0) +#endif + +static __always_inline struct pt_regs * +ftrace_fill_perf_regs (struct ftrace_regs *fregs,struct pt_regs *regs) +{ +arch_ftrace_fill_perf_regs (fregs, regs); +return regs; +} + +#else /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */ + +static __always_inline struct pt_regs * +ftrace_fill_perf_regs (struct ftrace_regs *fregs,struct pt_regs *regs) +{ +return &fregs->regs; +} + +#endif +/* * When true, the ftrace_regs_{get,set}_*() functions may be used on fregs. * Note: this can be true even when ftrace_get_regs() cannot provide a pt_regs.
Masami Hiramatsu (Google)
maintainerPATCH v14 10/19 tracing/fprobe: Enable fprobe events with CONFIG_DYNAMIC_FTRACE_WITH_ARGS
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Allow fprobe events to be enabled with CONFIG_DYNAMIC_FTRACE_WITH_ARGS. With this change, fprobe events mostly use ftrace_regs instead of pt_regs. Note that if the arch doesn't enable HAVE_PT_REGS_COMPAT_FTRACE_REGS, fprobe events will not be able to be used from perf.
---
Changes in v9:
- Copy store_trace_entry_data() as store_fprobe_entry_data() for fprobe.
Chagnes in v3:
- Use ftrace_regs_get_return_value().
Changes in v2:
- Define ftrace_regs_get_kernel_stack_nth() for !CONFIG_HAVE_REGS_AND_STACK_ACCESS_API.
Changes from previous series: Update against the new series.
--- include/linux/ftrace.h | 17 ++++++ kernel/trace/Kconfig | 1 kernel/trace/trace_fprobe.c | 107 +++++++++++++++++++++++++-------------- kernel/trace/trace_probe_tmpl.h | 2 - 4 files changed, 86 insertions(+), 41 deletions(-)
include/linux/ftrace.h +17 -0
@@ -255,6 +255,23 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs) frame_pointer(&(fregs)->regs)#endif +#ifdef CONFIG_HAVE_REGS_AND_STACK_ACCESS_API +static __always_inline unsigned long +ftrace_regs_get_kernel_stack_nth (struct ftrace_regs *fregs,unsigned int nth) +{ +unsigned long *stackp; + + stackp = (unsigned long *)ftrace_regs_get_stack_pointer (fregs); +if (((unsigned long )(stackp + nth) & ~(THREAD_SIZE -1 )) == + ((unsigned long )stackp & ~(THREAD_SIZE -1 ))) +return *(stackp + nth); + +return 0 ; +} +#else /* !CONFIG_HAVE_REGS_AND_STACK_ACCESS_API */ +#define ftrace_regs_get_kernel_stack_nth (fregs, nth) (0L) +#endif /* CONFIG_HAVE_REGS_AND_STACK_ACCESS_API */ +typedef void (*ftrace_func_t )(unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs);
kernel/trace/Kconfig +0 -1
@@ -680,7 +680,6 @@ config FPROBE_EVENTS select TRACING select PROBE_EVENTS select DYNAMIC_EVENTS - depends on DYNAMIC_FTRACE_WITH_REGS default y help This allows user to add tracing events on the function entry and
kernel/trace/trace_fprobe.c +68 -39
271 lines not on this page · raw message
kernel/trace/trace_probe_tmpl.h +1 -1
@@ -232,7 +232,7 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,/* Sum up total data length for dynamic arrays (strings) */ static nokprobe_inline int -__get_data_size (struct trace_probe *tp,struct pt_regs *regs,void *edata) +__get_data_size (struct trace_probe *tp,void *regs,void *edata) {struct probe_arg *arg;int i, len, ret =0 ;
Masami Hiramatsu (Google)
maintainerPATCH v14 11/19 bpf: Enable kprobe_multi feature if CONFIG_FPROBE is enabled
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Enable kprobe_multi feature if CONFIG_FPROBE is enabled. The pt_regs is converted from ftrace_regs by ftrace_partial_regs(), thus some registers may always returns 0. But it should be enough for function entry (access arguments) and exit (access return value).
---
Changes in v9:
- Avoid wasting memory for bpf_kprobe_multi_pt_regs when CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST=y
--- kernel/trace/bpf_trace.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-)
kernel/trace/bpf_trace.c +14 -13
@@ -2605,7 +2605,7 @@ struct bpf_session_run_ctx {void *data; }; -#if defined(CONFIG_FPROBE) && defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS) +#ifdef CONFIG_FPROBE struct bpf_kprobe_multi_link { struct bpf_link link; struct fprobe fp;
@@ -2628,6 +2628,13 @@ struct user_syms {char *buf; }; +#ifndef CONFIG_HAVE_PT_REGS_TO_FTRACE_REGS_CAST +static DEFINE_PER_CPU(struct pt_regs, bpf_kprobe_multi_pt_regs); +#define bpf_kprobe_multi_pt_regs_ptr () this_cpu_ptr(&bpf_kprobe_multi_pt_regs) +#else +#define bpf_kprobe_multi_pt_regs_ptr () (NULL) +#endif +static int copy_user_syms (struct user_syms *us,unsigned long __user *usyms,u32 cnt) {unsigned long __user usymbol;
@@ -2822,7 +2829,7 @@ static u64 bpf_kprobe_multi_entry_ip(struct bpf_run_ctx *ctx)static int kprobe_multi_link_prog_run (struct bpf_kprobe_multi_link *link, -unsigned long entry_ip,struct pt_regs *regs, +unsigned long entry_ip,struct ftrace_regs *fregs,bool is_return,void *data) {struct bpf_kprobe_multi_run_ctx run_ctx = {
@@ -2834,6 +2841,7 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link, .entry_ip = entry_ip, };struct bpf_run_ctx *old_run_ctx; +struct pt_regs *regs;int err;if (unlikely (__this_cpu_inc_return (bpf_prog_active) !=1 )) {
@@ -2844,6 +2852,7 @@ kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,migrate_disable ();rcu_read_lock (); + regs =ftrace_partial_regs (fregs,bpf_kprobe_multi_pt_regs_ptr ()); old_run_ctx =bpf_set_run_ctx (&run_ctx.session_ctx.run_ctx); err =bpf_prog_run (link->link.prog, regs);bpf_reset_run_ctx (old_run_ctx);
@@ -2860,15 +2869,11 @@ kprobe_multi_link_handler(struct fprobe *fp, unsigned long fentry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *data) { - struct pt_regs *regs = ftrace_get_regs(fregs); struct bpf_kprobe_multi_link *link;int err; -if (!regs) -return 0 ; - link = container_of(fp, struct bpf_kprobe_multi_link, fp); - err =kprobe_multi_link_prog_run (link,get_entry_ip (fentry_ip), regs, false, data); + err =kprobe_multi_link_prog_run (link,get_entry_ip (fentry_ip), fregs, false, data);return is_kprobe_session (link->link.prog) ? err :0 ; }
@@ -2878,13 +2883,9 @@ kprobe_multi_link_exit_handler(struct fprobe *fp, unsigned long fentry_ip, void *data) {struct bpf_kprobe_multi_link *link; -struct pt_regs *regs =ftrace_get_regs (fregs); - -if (!regs) -return ; link = container_of(fp, struct bpf_kprobe_multi_link, fp); -kprobe_multi_link_prog_run (link,get_entry_ip (fentry_ip), regs, true, data); +kprobe_multi_link_prog_run (link,get_entry_ip (fentry_ip), fregs, true, data); }static int symbols_cmp_r (const void *a,const void *b,const void *priv)
@@ -3145,7 +3146,7 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prkvfree (cookies); return err; } -#else /* !CONFIG_FPROBE || !CONFIG_DYNAMIC_FTRACE_WITH_REGS */ +#else /* !CONFIG_FPROBE */ int bpf_kprobe_multi_link_attach (const union bpf_attr *attr,struct bpf_prog *prog) {return -EOPNOTSUPP ;
Masami Hiramatsu (Google)
maintainerPATCH v14 12/19 ftrace: Add CONFIG_HAVE_FTRACE_GRAPH_FUNC
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Add CONFIG_HAVE_FTRACE_GRAPH_FUNC kconfig in addition to ftrace_graph_func macro check. This is for the other feature (e.g. FPROBE) which requires to access ftrace_regs from fgraph_ops::entryfunc() can avoid compiling if the fgraph can not pass the valid ftrace_regs.
---
Changes in v8:
- Newly added.
--- arch/arm64/Kconfig | 1 + arch/loongarch/Kconfig | 1 + arch/powerpc/Kconfig | 1 + arch/riscv/Kconfig | 1 + arch/x86/Kconfig | 1 + kernel/trace/Kconfig | 5 +++++ 6 files changed, 10 insertions(+)
arch/arm64/Kconfig +1 -0
@@ -208,6 +208,7 @@ config ARM64 select HAVE_SAMPLE_FTRACE_DIRECT_MULTI select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_GUP_FAST + select HAVE_FTRACE_GRAPH_FUNC select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_ERROR_INJECTION
arch/loongarch/Kconfig +1 -0
@@ -129,6 +129,7 @@ config LOONGARCH select HAVE_EFFICIENT_UNALIGNED_ACCESS if !ARCH_STRICT_ALIGN select HAVE_EXIT_THREAD select HAVE_GUP_FAST + select HAVE_FTRACE_GRAPH_FUNC select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION
arch/powerpc/Kconfig +1 -0
@@ -238,6 +238,7 @@ config PPC select HAVE_EBPF_JIT select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_GUP_FAST + select HAVE_FTRACE_GRAPH_FUNC select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_DESCRIPTORS if PPC64_ELF_ABI_V1
arch/riscv/Kconfig +1 -0
@@ -138,6 +138,7 @@ config RISCV select HAVE_DYNAMIC_FTRACE if !XIP_KERNEL && MMU && (CLANG_SUPPORTS_DYNAMIC_FTRACE || GCC_SUPPORTS_DYNAMIC_FTRACE) select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE + select HAVE_FTRACE_GRAPH_FUNC select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_FREGS
arch/x86/Kconfig +1 -0
@@ -228,6 +228,7 @@ config X86 select HAVE_EXIT_THREAD select HAVE_GUP_FAST select HAVE_FENTRY if X86_64 || DYNAMIC_FTRACE + select HAVE_FTRACE_GRAPH_FUNC if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_GRAPH_FREGS if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER if X86_32 || (X86_64 && DYNAMIC_FTRACE)
kernel/trace/Kconfig +5 -0
@@ -34,6 +34,11 @@ config HAVE_FUNCTION_GRAPH_TRACER config HAVE_FUNCTION_GRAPH_FREGS bool +config HAVE_FTRACE_GRAPH_FUNC + bool + help + True if ftrace_graph_func() is defined. + config HAVE_DYNAMIC_FTRACE bool help
Masami Hiramatsu (Google)
maintainerPATCH v14 13/19 fprobe: Rewrite fprobe on function-graph tracer
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Rewrite fprobe implementation on function-graph tracer. Major API changes are:
- 'nr_maxactive' field is deprecated.
- This depends on CONFIG_DYNAMIC_FTRACE_WITH_ARGS or !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS, and CONFIG_HAVE_FUNCTION_GRAPH_FREGS. So currently works only on x86_64.
- Currently the entry size is limited in 15 * sizeof(long).
- If there is too many fprobe exit handler set on the same function, it will fail to probe.
---
Changes in v14:
- Add ftrace_regs_get_return_addresss() for riscv.
Changes in v12:
- Skip updating ftrace hash if not required.
Changes in v9:
- Remove unneeded prototype of ftrace_regs_get_return_address().
- Fix entry data address calculation.
- Remove DIV_ROUND_UP() from hotpath.
Changes in v8:
- Use trace_func_graph_ret/ent_t for fgraph_ops.
- Update CONFIG_FPROBE dependencies.
- Add ftrace_regs_get_return_address() for each arch.
Changes in v3:
- Update for new reserve_data/retrieve_data API.
- Fix internal push/pop on fgraph data logic so that it can correctly save/restore the returning fprobes.
Changes in v2:
- Add more lockdep_assert_held(fprobe_mutex)
- Use READ_ONCE() and WRITE_ONCE() for fprobe_hlist_node::fp.
- Add NOKPROBE_SYMBOL() for the functions which is called from entry/exit callback.
--- arch/arm64/include/asm/ftrace.h | 6 arch/loongarch/include/asm/ftrace.h | 6 arch/powerpc/include/asm/ftrace.h | 6 arch/riscv/include/asm/ftrace.h | 5 arch/s390/include/asm/ftrace.h | 6 arch/x86/include/asm/ftrace.h | 6 include/linux/fprobe.h | 53 ++- kernel/trace/Kconfig | 8 kernel/trace/fprobe.c | 639 +++++++++++++++++++++++++---------- lib/test_fprobe.c | 45 -- 10 files changed, 535 insertions(+), 245 deletions(-)
arch/arm64/include/asm/ftrace.h +6 -0
@@ -132,6 +132,12 @@ ftrace_regs_get_frame_pointer(const struct ftrace_regs *fregs)return fregs->fp; } +static __always_inline unsigned long +ftrace_regs_get_return_address (const struct ftrace_regs *fregs) +{ +return fregs->lr; +} + static __always_inline struct pt_regs *ftrace_partial_regs (const struct ftrace_regs *fregs,struct pt_regs *regs) {
arch/loongarch/include/asm/ftrace.h +6 -0
@@ -80,6 +80,12 @@ ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)#define ftrace_regs_get_frame_pointer (fregs) \ ((fregs)->regs.regs[22]) +static __always_inline unsigned long +ftrace_regs_get_return_address (struct ftrace_regs *fregs) +{ +return *(unsigned long *)(fregs->regs.regs[1 ]); +} +#define ftrace_graph_func ftrace_graph_funcvoid ftrace_graph_func (unsigned long ip,unsigned long parent_ip,struct ftrace_ops *op,struct ftrace_regs *fregs);
arch/powerpc/include/asm/ftrace.h +6 -0
@@ -75,6 +75,12 @@ ftrace_regs_get_instruction_pointer(struct ftrace_regs *fregs)#define ftrace_regs_query_register_offset (name) \ regs_query_register_offset(name) +static __always_inline unsigned long +ftrace_regs_get_return_address (struct ftrace_regs *fregs) +{ +return fregs->regs.link; +} +struct ftrace_ops ;#define ftrace_graph_func ftrace_graph_func
arch/riscv/include/asm/ftrace.h +5 -0
@@ -182,6 +182,11 @@ static __always_inline unsigned long ftrace_regs_get_return_value(const struct freturn fregs->a0; } +static __always_inline unsigned long ftrace_regs_get_return_address (const struct ftrace_regs *fregs) +{ +return fregs->ra; +} + static __always_inline void ftrace_regs_set_return_value(struct ftrace_regs *fregs,unsigned long ret) {
arch/s390/include/asm/ftrace.h +6 -0
@@ -84,6 +84,12 @@ ftrace_regs_get_frame_pointer(struct ftrace_regs *fregs)return sp[0 ];/* return backchain */ } +static __always_inline unsigned long +ftrace_regs_get_return_address (const struct ftrace_regs *fregs) +{ +return fregs->regs.gprs[14 ]; +} +#define arch_ftrace_fill_perf_regs (fregs, _regs) do { \ (_regs)->psw.addr = (fregs)->regs.psw.addr; \ (_regs)->gprs[15] = (fregs)->regs.gprs[15]; \
arch/x86/include/asm/ftrace.h +6 -0
@@ -74,6 +74,12 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)#define ftrace_regs_get_frame_pointer (fregs) \ frame_pointer(&(fregs)->regs) +static __always_inline unsigned long +ftrace_regs_get_return_address (struct ftrace_regs *fregs) +{ +return *(unsigned long *)ftrace_regs_get_stack_pointer (fregs); +} +struct ftrace_ops ;#define ftrace_graph_func ftrace_graph_func void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
include/linux/fprobe.h +38 -15
83 lines not on this page · raw message
kernel/trace/Kconfig +3 -5
@@ -298,11 +298,9 @@ config DYNAMIC_FTRACE_WITH_ARGS config FPROBE bool "Kernel Function Probe (fprobe)" - depends on FUNCTION_TRACER - depends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS - depends on HAVE_PT_REGS_TO_FTRACE_REGS_CAST || !HAVE_DYNAMIC_FTRACE_WITH_ARGS - depends on HAVE_RETHOOK - select RETHOOK + depends on FUNCTION_GRAPH_TRACER + depends on HAVE_FUNCTION_GRAPH_FREGS && HAVE_FTRACE_GRAPH_FUNC + depends on DYNAMIC_FTRACE_WITH_ARGS default n help This option enables kernel function probe (fprobe) based on ftrace.
kernel/trace/fprobe.c +459 -180
769 lines not on this page · raw message
lib/test_fprobe.c +0 -45
85 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v14 14/19 tracing: Fix function timing profiler to initialize hashtable
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Since the new fgraph requires to initialize fgraph_ops.ops.func_hash before calling register_ftrace_graph(), initialize it with default (tracing all functions) parameter.
--- kernel/trace/ftrace.c | 4 ++++ 1 file changed, 4 insertions(+)
kernel/trace/ftrace.c +4 -0
@@ -885,6 +885,10 @@ static void profile_graph_return(struct ftrace_graph_ret *trace, }static struct fgraph_ops fprofiler_ops = { + .ops = { + .flags =FTRACE_OPS_FL_INITIALIZED , +INIT_OPS_HASH (fprofiler_ops.ops) + }, .entryfunc = &profile_graph_entry, .retfunc = &profile_graph_return, };
Masami Hiramatsu (Google)
maintainerPATCH v14 15/19 tracing/fprobe: Remove nr_maxactive from fprobe
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Remove depercated fprobe::nr_maxactive. This involves fprobe events to rejects the maxactive number.
---
Changes in v2:
- Newly added.
--- include/linux/fprobe.h | 2 -- kernel/trace/trace_fprobe.c | 44 ++++++------------------------------------- 2 files changed, 6 insertions(+), 40 deletions(-)
include/linux/fprobe.h +0 -2
@@ -54,7 +54,6 @@ struct fprobe_hlist {* @nmissed: The counter for missing events. * @flags: The status flag. * @entry_data_size: The private data storage size. -* @nr_maxactive: The max number of active functions. (*deprecated) * @entry_handler: The callback function for function entry. * @exit_handler: The callback function for function exit. * @hlist_array: The fprobe_hlist for fprobe search from IP hash table.
@@ -63,7 +62,6 @@ struct fprobe {unsigned long nmissed;unsigned int flags;size_t entry_data_size; -int nr_maxactive;fprobe_entry_cb entry_handler;fprobe_exit_cb exit_handler;
kernel/trace/trace_fprobe.c +6 -38
93 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v14 16/19 selftests: ftrace: Remove obsolate maxactive syntax check
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Since the fprobe event does not support maxactive anymore, stop testing the maxactive syntax error checking.
--- .../ftrace/test.d/dynevent/fprobe_syntax_errors.tc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc +1 -3
@@ -16,9 +16,7 @@ aarch64) REG=%r0 ;; esac -check_error 'f^100 vfs_read' # MAXACT_NO_KPROBE -check_error 'f^1a111 vfs_read' # BAD_MAXACT -check_error 'f^100000 vfs_read' # MAXACT_TOO_BIG +check_error 'f^100 vfs_read' # BAD_MAXACT check_error 'f ^non_exist_func' # BAD_PROBE_ADDR (enoent) check_error 'f ^vfs_read+10' # BAD_PROBE_ADDR
Masami Hiramatsu (Google)
maintainerPATCH v14 17/19 selftests/ftrace: Add a test case for repeating register/unregister fprobe
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
This test case repeats define and undefine the fprobe dynamic event to ensure that the fprobe does not cause any issue with such operations.
--- .../test.d/dynevent/add_remove_fprobe_repeat.tc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_repeat.tc
tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_repeat.tc new file +19 -0
@@ -0,0 +1,19 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - Repeating add/remove fprobe events +# requires: dynamic_events "f[:[<group>/][<event>]] <func-name>[%return] [<args>]":README + +echo 0 > events/enable +echo > dynamic_events + +PLACE=$FUNCTION_FORK +REPEAT_TIMES=64 + +for i in `seq 1 $REPEAT_TIMES`; do + echo "f:myevent $PLACE" >> dynamic_events + grep -q myevent dynamic_events + test -d events/fprobes/myevent + echo > dynamic_events +done + +clear_trace
Masami Hiramatsu (Google)
maintainerPATCH v14 18/19 Documentation: probes: Update fprobe on function-graph tracer
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Update fprobe documentation for the new fprobe on function-graph tracer. This includes some bahvior changes and pt_regs to ftrace_regs interface change.
---
Changes in v2:
- Update @fregs parameter explanation.
--- Documentation/trace/fprobe.rst | 42 ++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-)
Documentation/trace/fprobe.rst +27 -15
@@ -9,9 +9,10 @@ Fprobe - Function entry/exit probe Introduction ============ -Fprobe is a function entry/exit probe mechanism based on ftrace. -Instead of using ftrace full feature, if you only want to attach callbacks -on function entry and exit, similar to the kprobes and kretprobes, you can +Fprobe is a function entry/exit probe mechanism based on the function-graph +tracer. +Instead of tracing all functions, if you want to attach callbacks on specific +function entry and exit, similar to the kprobes and kretprobes, you can use fprobe. Compared with kprobes and kretprobes, fprobe gives faster instrumentation for multiple functions with single handler. This document describes how to use fprobe.
@@ -91,12 +92,14 @@ The prototype of the entry/exit callback function are as follows: .. code-block:: c - int entry_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct pt_regs *regs, void *entry_data); + int entry_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data); - void exit_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct pt_regs *regs, void *entry_data); + void exit_callback(struct fprobe *fp, unsigned long entry_ip, unsigned long ret_ip, struct ftrace_regs *fregs, void *entry_data); -Note that the @entry_ip is saved at function entry and passed to exit handler. -If the entry callback function returns !0, the corresponding exit callback will be cancelled. +Note that the @entry_ip is saved at function entry and passed to exit +handler. +If the entry callback function returns !0, the corresponding exit callback +will be cancelled. @fp This is the address of `fprobe` data structure related to this handler.
@@ -112,12 +115,10 @@ If the entry callback function returns !0, the corresponding exit callback will This is the return address that the traced function will return to, somewhere in the caller. This can be used at both entry and exit. -@regs - This is the `pt_regs` data structure at the entry and exit. Note that - the instruction pointer of @regs may be different from the @entry_ip - in the entry_handler. If you need traced instruction pointer, you need - to use @entry_ip. On the other hand, in the exit_handler, the instruction - pointer of @regs is set to the current return address. +@fregs + This is the `ftrace_regs` data structure at the entry and exit. This + includes the function parameters, or the return values. So user can + access thos values via appropriate `ftrace_regs_*` APIs. @entry_data This is a local storage to share the data between entry and exit handlers.
@@ -125,6 +126,17 @@ If the entry callback function returns !0, the corresponding exit callback will and `entry_data_size` field when registering the fprobe, the storage is allocated and passed to both `entry_handler` and `exit_handler`. +Entry data size and exit handlers on the same function +====================================================== + +Since the entry data is passed via per-task stack and it is has limited size, +the entry data size per probe is limited to `15 * sizeof(long)`. You also need +to take care that the different fprobes are probing on the same function, this +limit becomes smaller. The entry data size is aligned to `sizeof(long)` and +each fprobe which has exit handler uses a `sizeof(long)` space on the stack, +you should keep the number of fprobes on the same function as small as +possible. + Share the callbacks with kprobes ================================
@@ -165,8 +177,8 @@ This counter counts up when; - fprobe fails to take ftrace_recursion lock. This usually means that a function which is traced by other ftrace users is called from the entry_handler. - - fprobe fails to setup the function exit because of the shortage of rethook - (the shadow stack for hooking the function return.) + - fprobe fails to setup the function exit because of failing to allocate the + data buffer from the per-task shadow stack. The `fprobe::nmissed` field counts up in both cases. Therefore, the former skips both of entry and exit callback and the latter skips the exit
Masami Hiramatsu (Google)
maintainerPATCH v14 19/19 fgraph: Skip recording calltime/rettime if it is not nneeded
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Skip recording calltime and rettime if the fgraph_ops does not need it. This is a kind of performance optimization for fprobe. Since the fprobe user does not use these entries, recording timestamp in fgraph is just a overhead (e.g. eBPF, ftrace). So introduce the skip_timestamp flag, and all fgraph_ops sets this flag, skip recording calltime and rettime.
Here is the performance results measured by
tools/testing/selftests/bpf/benchs/run_bench_trigger.sh
Without this:
kprobe-multi : 5.700 ± 0.065M/s kretprobe-multi: 4.239 ± 0.006M/s
With skip-timestamp:
kprobe-multi : 6.265 ± 0.033M/s +9.91% kretprobe-multi: 4.758 ± 0.009M/s +12.24%
---
Changes in v11:
- Simplify it to be symmetric on push and pop. (Thus the timestamp getting place is a bit shifted.)
Changes in v10:
- Add likely() to skipping timestamp.
Changes in v9:
- Newly added.
--- include/linux/ftrace.h | 2 ++ kernel/trace/fgraph.c | 36 +++++++++++++++++++++++++++++++++--- kernel/trace/fprobe.c | 1 + 3 files changed, 36 insertions(+), 3 deletions(-)
include/linux/ftrace.h +2 -0
@@ -1160,6 +1160,8 @@ struct fgraph_ops {void *private;trace_func_graph_ent_t saved_func;int idx; +/* If skip_timestamp is true, this does not record timestamps. */ +bool skip_timestamp; };void *fgraph_reserve_data (int idx,int size_bytes);
kernel/trace/fgraph.c +33 -3
@@ -174,6 +174,7 @@ int ftrace_graph_active;static struct fgraph_ops *fgraph_array[FGRAPH_ARRAY_SIZE ];static unsigned long fgraph_array_bitmask; +static bool fgraph_skip_timestamp;/* LRU index table for fgraph_array */ static int fgraph_lru_table[FGRAPH_ARRAY_SIZE ];
@@ -557,7 +558,11 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func,return -EBUSY ; } - calltime =trace_clock_local (); +/* This is not really 'likely' but for keeping the least path to be faster. */ +if (likely (fgraph_skip_timestamp)) + calltime =0LL ; +else + calltime =trace_clock_local (); offset =READ_ONCE (current->curr_ret_stack); ret_stack =RET_STACK (current, offset);
@@ -728,6 +733,12 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, *ret = ret_stack->ret; trace->func = ret_stack->func; trace->calltime = ret_stack->calltime; +/* This is not really 'likely' but for keeping the least path to be faster. */ +if (likely (!trace->calltime)) + trace->rettime =0LL ; +else + trace->rettime =trace_clock_local (); + trace->overrun =atomic_read (¤t->trace_overrun); trace->depth = current->curr_ret_depth;/*
@@ -788,7 +799,6 @@ __ftrace_return_to_handler(struct ftrace_regs *fregs, unsigned long frame_pointereturn (unsigned long )panic; } - trace.rettime =trace_clock_local ();if (fregs)ftrace_regs_set_instruction_pointer (fregs, ret);
@@ -1248,6 +1258,24 @@ static void ftrace_graph_disable_direct(bool disable_branch) fgraph_direct_gops = &fgraph_stub; } +static void update_fgraph_skip_timestamp (void ) +{ +int i; + +for (i =0 ; i <FGRAPH_ARRAY_SIZE ; i++) { +struct fgraph_ops *gops = fgraph_array[i]; + +if (gops == &fgraph_stub) +continue ; + +if (!gops->skip_timestamp) { + fgraph_skip_timestamp = false; +return ; + } + } + fgraph_skip_timestamp = true; +} +int register_ftrace_graph (struct fgraph_ops *gops) {int command =0 ;
@@ -1271,6 +1299,7 @@ int register_ftrace_graph(struct fgraph_ops *gops) gops->idx = i; ftrace_graph_active++; +update_fgraph_skip_timestamp ();if (ftrace_graph_active ==2 )ftrace_graph_disable_direct (true);
@@ -1303,6 +1332,7 @@ int register_ftrace_graph(struct fgraph_ops *gops) ftrace_graph_active--; gops->saved_func =NULL ;fgraph_lru_release_index (i); +update_fgraph_skip_timestamp (); } out:mutex_unlock (&ftrace_lock);
@@ -1326,8 +1356,8 @@ void unregister_ftrace_graph(struct fgraph_ops *gops) goto out; fgraph_array[gops->idx] = &fgraph_stub; - ftrace_graph_active--; +update_fgraph_skip_timestamp ();if (!ftrace_graph_active) command =FTRACE_STOP_FUNC_RET ;
kernel/trace/fprobe.c +1 -0
@@ -345,6 +345,7 @@ NOKPROBE_SYMBOL(fprobe_return);static struct fgraph_ops fprobe_graph_ops = { .entryfunc = fprobe_entry, .retfunc = fprobe_return, + .skip_timestamp = true, };static int fprobe_graph_active;
Steven Rostedt
maintainerRe: [PATCH v14 19/19] fgraph: Skip recording calltime/rettime if it is not nneeded
Quoted 15 lines · On Fri, 13 Sep 2024 00:11:38 +0900 "Masami Hiramatsu (Google)"…
From: Masami Hiramatsu (Google) <[email protected]>
Skip recording calltime and rettime if the
fgraph_opsdoes not need it. This is a kind of performance optimization for fprobe. Since the fprobe user does not use these entries, recording timestamp in fgraph is just a overhead (e.g. eBPF, ftrace). So introduce theskip_timestampflag, and allfgraph_opssets this flag, skip recording calltime and rettime.Here is the performance results measured by
tools/testing/selftests/bpf/benchs/run_bench_trigger.shWithout this:
kprobe-multi : 5.700 ± 0.065M/s kretprobe-multi: 4.239 ± 0.006M/s
Quoted 6 lines
With skip-timestamp:
kprobe-multi : 6.265 ± 0.033M/s +9.91% kretprobe-multi: 4.758 ± 0.009M/s +12.24%Suggested-by Jiri Olsa <[email protected]>Signed-off-by Masami Hiramatsu (Google) <[email protected]>
Can you try this series instead:
https://lore.kernel.org/all/[email protected]/
I rather get rid of the calltime completely from the generic logic, and that series does just that.
That series only replaces this patch and can be applied before this series (or after).
-- Steve
A message that is not in the archive (sent off-list or not yet received): 0170cd7d95df0583770c385c1e11bd27dfacf618b71b6e723f0952efc0ce9040@mail.kernel.org
+ BPF ML
Quoted 33 lines · On Thu, Sep 12, 2024 at 8:35 AM <[email protected]> wrote:
Dear patch submitter,
CI has tested the following submission:
Status: FAILURE Name: [v14,00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Patchwork: https://patchwork.kernel.org/project/netdevbpf/list/?series=889822&state=* Matrix: https://github.com/kernel-patches/bpf/actions/runs/10833792984Failed jobs:
test_progs-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791397
test_progs_no_alu32-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791836
test_progs-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757062
test_progs_no_alu32-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757809First
test_progsfailure (test_progs-aarch64-gcc):
#132kprobe_multi_testmod_test
serial_test_kprobe_multi_testmod_test:PASS:load_kallsyms_local 0 nsec
#132/1 kprobe_multi_testmod_test/testmod_attach_api_syms test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsec trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpectedkprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1 #132/2 kprobe_multi_testmod_test/testmod_attach_api_addrs test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsec
Quoted 9 lines
trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpected
kprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1
Seems like this selftest is still broken. Please let me know if you need help with building and running BPF selftests to reproduce this locally.
Quoted 4 lines
Please note: this email is coming from an unmonitored mailbox. If you have questions or feedback, please reach out to the Meta Kernel CI team at [email protected].
Masami Hiramatsu (Google)
maintainerQuoted 46 lines · On Thu, 12 Sep 2024 11:41:17 -0700 Andrii Nakryiko <[email protected]>…
+ BPF ML
Quoted 33 lines · On Thu, Sep 12, 2024 at 8:35 AM <[email protected]> wrote:
Dear patch submitter,
CI has tested the following submission:
Status: FAILURE Name: [v14,00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Patchwork: https://patchwork.kernel.org/project/netdevbpf/list/?series=889822&state=* Matrix: https://github.com/kernel-patches/bpf/actions/runs/10833792984Failed jobs:
test_progs-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791397
test_progs_no_alu32-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791836
test_progs-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757062
test_progs_no_alu32-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757809First
test_progsfailure (test_progs-aarch64-gcc):
#132kprobe_multi_testmod_test
serial_test_kprobe_multi_testmod_test:PASS:load_kallsyms_local 0 nsec
#132/1 kprobe_multi_testmod_test/testmod_attach_api_syms test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsec trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpectedkprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1 #132/2 kprobe_multi_testmod_test/testmod_attach_api_addrs test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsecQuoted 9 lines
trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpected
kprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1
Quoted 3 lines
Seems like this selftest is still broken. Please let me know if you need help with building and running BPF selftests to reproduce this locally.
Thanks, It will be helpful. Also I would like to know, is there any debug mode (like print more debug logs)?
Thank you,
Quoted 5 lines
Quoted 5 lines
Please note: this email is coming from an unmonitored mailbox. If you have questions or feedback, please reach out to the Meta Kernel CI team at [email protected].
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Quoted 50 lines · Masami Hiramatsu
Quoted 46 lines · On Thu, 12 Sep 2024 11:41:17 -0700 Andrii Nakryiko <[email protected]>…
+ BPF ML
Quoted 42 lines · On Thu, Sep 12, 2024 at 8:35 AM <[email protected]> wrote:
Dear patch submitter,
CI has tested the following submission:
Status: FAILURE Name: [v14,00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Patchwork: https://patchwork.kernel.org/project/netdevbpf/list/?series=889822&state=* Matrix: https://github.com/kernel-patches/bpf/actions/runs/10833792984Failed jobs:
test_progs-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791397
test_progs_no_alu32-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791836
test_progs-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757062
test_progs_no_alu32-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757809First
test_progsfailure (test_progs-aarch64-gcc):
#132kprobe_multi_testmod_test
serial_test_kprobe_multi_testmod_test:PASS:load_kallsyms_local 0 nsec
#132/1 kprobe_multi_testmod_test/testmod_attach_api_syms test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsec trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpectedkprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1 #132/2 kprobe_multi_testmod_test/testmod_attach_api_addrs test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsec trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpectedkprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1
Quoted 6 lines
Quoted 3 lines
Seems like this selftest is still broken. Please let me know if you need help with building and running BPF selftests to reproduce this locally.
Thanks, It will be helpful. Also I would like to know, is there any debug mode (like print more debug logs)?
So first of all, the assumption is that you will build a most recent kernel with Kconfig values set from tools/testing/selftests/bpf/config, so make sure you append that to your kernel config. Then build the kernel, BPF selftests' Makefile tries to find latest built kernel (according to KBUILD_OUTPUT/O/etc).
Now to building BPF selftests:
$ cd tools/testing/selftests/bpf $ make -j$(nproc)
You'll need decently recent Clang and a few dependent packages. At least elfutils-devel, libzstd-devel, but there might be a few more which I never can remember.
Once everything is built, you can run the failing test with
$ sudo ./test_progs -t kprobe_multi_testmod_test -v
The source code for user space part for that test is in prog_tests/kprobe_multi_testmod_test.c and BPF-side is in progs/kprobe_multi.c.
Taking failing output from the test:
Quoted 1 line
Quoted 1 line
Quoted 1 line
kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpected
kretprobe_test3_result: actual 0 != expected 1
kretprobe_test3_result is a sort of identifier for a test condition, you can find a corresponding line in user space .c file grepping for that:
ASSERT_EQ(skel->bss->kretprobe_testmod_test3_result, 1, "kretprobe_test3_result");
Most probably the problem is in:
__u64 addr = bpf_get_func_ip(ctx);
which is then checked as
if ((const void *) addr == &bpf_testmod_fentry_test3)
With your patches this doesn't match anymore.
Hopefully the above gives you some pointers, let me know if you run into any problems.
Quoted 12 lines
Thank you,
Quoted 5 lines
Quoted 5 lines
Please note: this email is coming from an unmonitored mailbox. If you have questions or feedback, please reach out to the Meta Kernel CI team at [email protected].
--
Masami Hiramatsu (Google) <[email protected]>
Masami Hiramatsu (Google)
maintainerQuoted 73 lines · On Thu, 12 Sep 2024 18:55:40 -0700 Andrii Nakryiko <[email protected]>…
Quoted 50 lines · Masami Hiramatsu
Quoted 46 lines · On Thu, 12 Sep 2024 11:41:17 -0700 Andrii Nakryiko <[email protected]>…
+ BPF ML
Quoted 42 lines · On Thu, Sep 12, 2024 at 8:35 AM <[email protected]> wrote:
Dear patch submitter,
CI has tested the following submission:
Status: FAILURE Name: [v14,00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Patchwork: https://patchwork.kernel.org/project/netdevbpf/list/?series=889822&state=* Matrix: https://github.com/kernel-patches/bpf/actions/runs/10833792984Failed jobs:
test_progs-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791397
test_progs_no_alu32-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791836
test_progs-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757062
test_progs_no_alu32-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757809First
test_progsfailure (test_progs-aarch64-gcc):
#132kprobe_multi_testmod_test
serial_test_kprobe_multi_testmod_test:PASS:load_kallsyms_local 0 nsec
#132/1 kprobe_multi_testmod_test/testmod_attach_api_syms test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsec trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpectedkprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1 #132/2 kprobe_multi_testmod_test/testmod_attach_api_addrs test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsec trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpectedkprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1Quoted 6 lines
Quoted 3 lines
Seems like this selftest is still broken. Please let me know if you need help with building and running BPF selftests to reproduce this locally.
Thanks, It will be helpful. Also I would like to know, is there any debug mode (like print more debug logs)?
So first of all, the assumption is that you will build a most recent kernel with
Kconfigvalues set fromtools/testing/selftests/bpf/config, so make sure you append that to your kernel config. Then build the kernel, BPF selftests'Makefiletries to find latest built kernel (according to KBUILD_OUTPUT/O/etc).Now to building BPF selftests:
$ cd tools/testing/selftests/bpf $ make -j$(nproc)You'll need decently recent Clang and a few dependent packages. At least elfutils-devel, libzstd-devel, but there might be a few more which I never can remember.
Quoted 7 lines
Once everything is built, you can run the failing test with
$ sudo ./test_progs -t kprobe_multi_testmod_test -vThe source code for user space part for that test is in
prog_tests/kprobe_multi_testmod_test.cand BPF-side is inprogs/kprobe_multi.c.
Thanks for the information!
Quoted 9 lines
Taking failing output from the test:
Quoted 1 line
Quoted 1 line
Quoted 1 line
kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpected
kretprobe_test3_result: actual 0 != expected 1
kretprobe_test3_resultis a sort of identifier for a test condition, you can find a corresponding line in user space .c file grepping for that:
Quoted 6 lines
ASSERT_EQ(skel->bss->kretprobe_testmod_test3_result, 1, "kretprobe_test3_result");Most probably the problem is in:
__u64addr =bpf_get_func_ip(ctx);
Yeah, and as I replyed to another thread, the problem is that the ftrace entry_ip is not symbol ip.
We have ftrace_call_adjust() arch function for reverse direction (symbol ip to ftrace entry ip) but what we need here is the reverse translate function (ftrace entry to symbol)
The easiest way is to use kallsyms to find it, but this is a bit costful (but it just increase bsearch in several levels). Other possible options are
- Change bpf_kprobe_multi_addrs_cmp() to accept a range of address. [sym_addr, sym_addr + offset) returns true, bpf_kprobe_multi_cookie() can find the entry address. The offset depends on arch, but 16 is enough.
- Change bpf_kprobe_multi_addrs_cmp() to call ftrace_call_adjust() before comparing. This may take a cost but find actual entry address.
- (Cached method) when making link->addrs, make a link->adj_addrs array too, where adj_addrs[i] == ftrace_call_adjust(addrs[i]).
Let me try the 3rd one. It may consume more memory but the fastest solution.
Thank you,
Quoted 7 lines
which is then checked as
if ((const void *) addr ==
&bpf_testmod_fentry_test3)With your patches this doesn't match anymore.
It actually enables the fprobe on those architectures, so without my series, those test should be skipped.
Thank you,
Quoted 17 lines
Hopefully the above gives you some pointers, let me know if you run into any problems.
Quoted 12 lines
Thank you,
Quoted 5 lines
Quoted 5 lines
Please note: this email is coming from an unmonitored mailbox. If you have questions or feedback, please reach out to the Meta Kernel CI team at [email protected].
--
Masami Hiramatsu (Google) <[email protected]>
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Masami Hiramatsu (Google)
maintainerQuoted 23 lines · On Fri, 13 Sep 2024 17:59:35 +0900 Masami Hiramatsu (Google)…
Quoted 9 lines
Taking failing output from the test:
Quoted 1 line
Quoted 1 line
Quoted 1 line
kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpected
kretprobe_test3_result: actual 0 != expected 1
kretprobe_test3_resultis a sort of identifier for a test condition, you can find a corresponding line in user space .c file grepping for that:Quoted 6 lines
ASSERT_EQ(skel->bss->kretprobe_testmod_test3_result, 1, "kretprobe_test3_result");Most probably the problem is in:
__u64addr =bpf_get_func_ip(ctx);Yeah, and as I replyed to another thread, the problem is that the ftrace
entry_ipis not symbol ip.We have
ftrace_call_adjust()arch function for reverse direction (symbol ip to ftrace entry ip) but what we need here is the reverse translate function (ftrace entry to symbol)
Quoted 8 lines
The easiest way is to use kallsyms to find it, but this is a bit costful (but it just increase bsearch in several levels). Other possible options are
- Change
bpf_kprobe_multi_addrs_cmp()to accept a range of address. [sym_addr,sym_addr+ offset) returns true,bpf_kprobe_multi_cookie()can find the entry address. The offset depends on arch, but 16 is enough.
Oops. no, this bpf_kprobe_multi_cookie() is used only for storing test data. Not a general problem solving. (I saw kprobe_multi_check())
So solving problem is much costly, we need to put more arch- dependent in bpf_trace, and make sure it does reverse translation of ftrace_call_adjust(). (this means if you expand arch support, you need to add such implementation)
Thank you,
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Masami Hiramatsu (Google)
maintainerQuoted 35 lines · On Fri, 13 Sep 2024 21:45:15 +0900 Masami Hiramatsu (Google)…
Quoted 23 lines · On Fri, 13 Sep 2024 17:59:35 +0900 Masami Hiramatsu (Google)…
Quoted 9 lines
Taking failing output from the test:
Quoted 1 line
Quoted 1 line
Quoted 1 line
kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpected
kretprobe_test3_result: actual 0 != expected 1
kretprobe_test3_resultis a sort of identifier for a test condition, you can find a corresponding line in user space .c file grepping for that:Quoted 6 lines
ASSERT_EQ(skel->bss->kretprobe_testmod_test3_result, 1, "kretprobe_test3_result");Most probably the problem is in:
__u64addr =bpf_get_func_ip(ctx);Yeah, and as I replyed to another thread, the problem is that the ftrace
entry_ipis not symbol ip.We have
ftrace_call_adjust()arch function for reverse direction (symbol ip to ftrace entry ip) but what we need here is the reverse translate function (ftrace entry to symbol)Quoted 8 lines
The easiest way is to use kallsyms to find it, but this is a bit costful (but it just increase bsearch in several levels). Other possible options are
- Change
bpf_kprobe_multi_addrs_cmp()to accept a range of address. [sym_addr,sym_addr+ offset) returns true,bpf_kprobe_multi_cookie()can find the entry address. The offset depends on arch, but 16 is enough.
Quoted 7 lines
Oops. no, this
bpf_kprobe_multi_cookie()is used only for storing test data. Not a general problem solving. (I sawkprobe_multi_check())So solving problem is much costly, we need to put more arch- dependent in
bpf_trace, and make sure it does reverse translation offtrace_call_adjust(). (this means if you expand arch support, you need to add such implementation)
OK, can you try this one?
From 81bc599911507215aa9faa1077a601880cbd654a Mon Sep 17 00:00:00 2001
From: "Masami Hiramatsu (Google)" <[email protected]>
Date: Fri, 13 Sep 2024 21:43:46 +0900
Subject: [PATCH] bpf: Add get_entry_ip() for arm64
Add get_entry_ip() implementation for arm64. This is based on the information in ftrace_call_adjust() for arm64.
--- kernel/trace/bpf_trace.c | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)
kernel/trace/bpf_trace.c +64 -0
@@ -1066,6 +1066,70 @@ static unsigned long get_entry_ip(unsigned long fentry_ip) fentry_ip -=ENDBR_INSN_SIZE ;return fentry_ip; } +#elif defined (CONFIG_ARM64 ) +#include <asm/insn.h> + +static unsigned long get_entry_ip (unsigned long fentry_ip) +{ +u32 insn; + +/* +* When using patchable-function-entry without pre-function NOPS, ftrace +* entry is the address of the first NOP after the function entry point. +* +* The compiler has either generated: +* +* func+00: func: NOP // To be patched to MOV X9, LR +* func+04: NOP // To be patched to BL <caller> +* +* Or: +* +* func-04: BTI C +* func+00: func: NOP // To be patched to MOV X9, LR +* func+04: NOP // To be patched to BL <caller> +* +* The fentry_ip is the address of `BL <caller>` which is at `func + 4` +* bytes in either case. +*/ +if (!IS_ENABLED (CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS )) +return fentry_ip -AARCH64_INSN_SIZE ; + +/* +* When using patchable-function-entry with pre-function NOPs, BTI is +* a bit different. +* +* func+00: func: NOP // To be patched to MOV X9, LR +* func+04: NOP // To be patched to BL <caller> +* +* Or: +* +* func+00: func: BTI C +* func+04: NOP // To be patched to MOV X9, LR +* func+08: NOP // To be patched to BL <caller> +* +* The fentry_ip is the address of `BL <caller>` which is at either +* `func + 4` or `func + 8` depends on whether there is a BTI. +*/ + +/* If there is no BTI, the func address should be one instruction before. */ +if (!IS_ENABLED (CONFIG_ARM64_BTI_KERNEL )) +return fentry_ip -AARCH64_INSN_SIZE ; + +/* We want to be extra safe in case entry ip is on the page edge, +* but otherwise we need to avoid get_kernel_nofault()'s overhead. +*/ +if ((fentry_ip & ~PAGE_MASK ) <AARCH64_INSN_SIZE *2 ) { +if (get_kernel_nofault (insn, (u32 *)(fentry_ip -AARCH64_INSN_SIZE *2 ))) +return fentry_ip -AARCH64_INSN_SIZE ; + }else { + insn = *(u32 *)(fentry_ip -AARCH64_INSN_SIZE *2 ); + } + +if (aarch64_insn_is_bti (le32_to_cpu ((__le32 )insn))) +return fentry_ip -AARCH64_INSN_SIZE *2 ; + +return fentry_ip -AARCH64_INSN_SIZE ; +}#else #define get_entry_ip (fentry_ip) fentry_ip#endif
Signature · 6 lines
2.34.1 -- Masami Hiramatsu (Google) <[email protected]>
Quoted 42 lines · Masami Hiramatsu
Quoted 38 lines · On Fri, 13 Sep 2024 21:45:15 +0900 Masami Hiramatsu (Google)…
Quoted 23 lines · On Fri, 13 Sep 2024 17:59:35 +0900 Masami Hiramatsu (Google)…
Quoted 9 lines
Taking failing output from the test:
Quoted 1 line
Quoted 1 line
Quoted 1 line
kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpected
kretprobe_test3_result: actual 0 != expected 1
kretprobe_test3_resultis a sort of identifier for a test condition, you can find a corresponding line in user space .c file grepping for that:Quoted 6 lines
ASSERT_EQ(skel->bss->kretprobe_testmod_test3_result, 1, "kretprobe_test3_result");Most probably the problem is in:
__u64addr =bpf_get_func_ip(ctx);Yeah, and as I replyed to another thread, the problem is that the ftrace
entry_ipis not symbol ip.We have
ftrace_call_adjust()arch function for reverse direction (symbol ip to ftrace entry ip) but what we need here is the reverse translate function (ftrace entry to symbol)Quoted 8 lines
The easiest way is to use kallsyms to find it, but this is a bit costful (but it just increase bsearch in several levels). Other possible options are
- Change
bpf_kprobe_multi_addrs_cmp()to accept a range of address. [sym_addr,sym_addr+ offset) returns true,bpf_kprobe_multi_cookie()can find the entry address. The offset depends on arch, but 16 is enough.Oops. no, this
bpf_kprobe_multi_cookie()is used only for storing test data. Not a general problem solving. (I sawkprobe_multi_check())
Quoted 7 lines
Quoted 4 lines
So solving problem is much costly, we need to put more arch- dependent in
bpf_trace, and make sure it does reverse translation offtrace_call_adjust(). (this means if you expand arch support, you need to add such implementation)OK, can you try this one?
I'm running out of time today, so I won't have time to try this, sorry.
But see my last reply, I think adjusting link->addrs once before attachment is the way to go. It gives us fast and simple lookups at runtime.
In my last reply I assumed that we won't need to keep a copy of original addrs (because we can dynamically adjust for bpf_kprobe_multi_link_fill_link_info()), but I now realize that we do need that for bpf_get_func_ip() anyways.
Still, I'd rather have an extra link->adj_addrs with a copy and do a quick and simple lookup at runtime. So I suggest going with that. At the very worst case it's a few kilobytes of memory for thousands of attached functions, no big deal, IMO.
It's vastly better than maintaining arch-specific reverse of ftrace_call_adjust(), isn't it?
Jiri, any opinion here?
Quoted 96 lines
From
81bc599911507215aa9faa1077a601880cbd654aMon Sep 17 00:00:00 2001
From: "Masami Hiramatsu (Google)" <[email protected]>
Date: Fri, 13 Sep 2024 21:43:46 +0900
Subject: [PATCH] bpf: Addget_entry_ip()for arm64Add
get_entry_ip()implementation for arm64. This is based on the information inftrace_call_adjust()for arm64.Signed-off-by Masami Hiramatsu (Google) <[email protected]>--- kernel/trace/bpf_trace.c | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)kernel/trace/bpf_trace.c +64
-1@@ -1066,6 +1066,70 @@ static unsigned long get_entry_ip(unsigned long fentry_ip) fentry_ip -=ENDBR_INSN_SIZE ;return fentry_ip; } +#elif defined (CONFIG_ARM64 ) +#include <asm/insn.h> + +static unsigned long get_entry_ip (unsigned long fentry_ip) +{ +u32 insn; + +/* +* When using patchable-function-entry without pre-function NOPS, ftrace +* entry is the address of the first NOP after the function entry point. +* +* The compiler has either generated: +* +* func+00: func: NOP // To be patched to MOV X9, LR +* func+04: NOP // To be patched to BL <caller> +* +* Or: +* +* func-04: BTI C +* func+00: func: NOP // To be patched to MOV X9, LR +* func+04: NOP // To be patched to BL <caller> +* +* The fentry_ip is the address of `BL <caller>` which is at `func + 4` +* bytes in either case. +*/ +if (!IS_ENABLED (CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS )) +return fentry_ip -AARCH64_INSN_SIZE ; + +/* +* When using patchable-function-entry with pre-function NOPs, BTI is +* a bit different. +* +* func+00: func: NOP // To be patched to MOV X9, LR +* func+04: NOP // To be patched to BL <caller> +* +* Or: +* +* func+00: func: BTI C +* func+04: NOP // To be patched to MOV X9, LR +* func+08: NOP // To be patched to BL <caller> +* +* The fentry_ip is the address of `BL <caller>` which is at either +* `func + 4` or `func + 8` depends on whether there is a BTI. +*/ + +/* If there is no BTI, the func address should be one instruction before. */ +if (!IS_ENABLED (CONFIG_ARM64_BTI_KERNEL )) +return fentry_ip -AARCH64_INSN_SIZE ; + +/* We want to be extra safe in case entry ip is on the page edge, +* but otherwise we need to avoid get_kernel_nofault()'s overhead. +*/ +if ((fentry_ip & ~PAGE_MASK ) <AARCH64_INSN_SIZE *2 ) { +if (get_kernel_nofault (insn, (u32 *)(fentry_ip -AARCH64_INSN_SIZE *2 ))) +return fentry_ip -AARCH64_INSN_SIZE ; + }else { + insn = *(u32 *)(fentry_ip -AARCH64_INSN_SIZE *2 ); + } + +if (aarch64_insn_is_bti (le32_to_cpu ((__le32 )insn))) +return fentry_ip -AARCH64_INSN_SIZE *2 ; + +return fentry_ip -AARCH64_INSN_SIZE ; +}#else #define get_entry_ip (fentry_ip) fentry_ip#endif --2.34.1
--
Masami Hiramatsu (Google) <[email protected]>
Masami Hiramatsu (Google)
maintainerQuoted 53 lines · On Fri, 13 Sep 2024 14:23:38 -0700 Andrii Nakryiko <[email protected]>…
Quoted 42 lines · Masami Hiramatsu
Quoted 38 lines · On Fri, 13 Sep 2024 21:45:15 +0900 Masami Hiramatsu (Google)…
Quoted 23 lines · On Fri, 13 Sep 2024 17:59:35 +0900 Masami Hiramatsu (Google)…
Quoted 9 lines
Taking failing output from the test:
Quoted 1 line
Quoted 1 line
Quoted 1 line
kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpected
kretprobe_test3_result: actual 0 != expected 1
kretprobe_test3_resultis a sort of identifier for a test condition, you can find a corresponding line in user space .c file grepping for that:Quoted 6 lines
ASSERT_EQ(skel->bss->kretprobe_testmod_test3_result, 1, "kretprobe_test3_result");Most probably the problem is in:
__u64addr =bpf_get_func_ip(ctx);Yeah, and as I replyed to another thread, the problem is that the ftrace
entry_ipis not symbol ip.We have
ftrace_call_adjust()arch function for reverse direction (symbol ip to ftrace entry ip) but what we need here is the reverse translate function (ftrace entry to symbol)Quoted 8 lines
The easiest way is to use kallsyms to find it, but this is a bit costful (but it just increase bsearch in several levels). Other possible options are
- Change
bpf_kprobe_multi_addrs_cmp()to accept a range of address. [sym_addr,sym_addr+ offset) returns true,bpf_kprobe_multi_cookie()can find the entry address. The offset depends on arch, but 16 is enough.Oops. no, this
bpf_kprobe_multi_cookie()is used only for storing test data. Not a general problem solving. (I sawkprobe_multi_check())Quoted 7 lines
Quoted 4 lines
So solving problem is much costly, we need to put more arch- dependent in
bpf_trace, and make sure it does reverse translation offtrace_call_adjust(). (this means if you expand arch support, you need to add such implementation)OK, can you try this one?
I'm running out of time today, so I won't have time to try this, sorry.
Quoted 8 lines
But see my last reply, I think adjusting
link->addrsonce before attachment is the way to go. It gives us fast and simple lookups at runtime.In my last reply I assumed that we won't need to keep a copy of original addrs (because we can dynamically adjust for
bpf_kprobe_multi_link_fill_link_info()), but I now realize that we do need that forbpf_get_func_ip()anyways.
Yes, that's the point.
Quoted 5 lines
Still, I'd rather have an extra
link->adj_addrswith a copy and do a quick and simple lookup at runtime. So I suggest going with that. At the very worst case it's a few kilobytes of memory for thousands of attached functions, no big deal, IMO.
But if you look carefully the below code, it should be faster than looking up from link->adj_addrs since most of conditions are build-time condition. (Only when the kernel enables BTI and the function entry(+8bytes) is on the page boundary, we will call get_kernel_nofault(), but it is very rare case.)
The only one concern about the below code is that is architecture dependent. It should be provided by arch/arm64/kernel/ftrace.c.
Quoted 3 lines
It's vastly better than maintaining arch-specific reverse of
ftrace_call_adjust(), isn't it?
Yes, it should be (and x86_64 ENDBR part too).
Quoted 2 lines
Jiri, any opinion here?
Thank you,
Quoted 97 lines
Quoted 97 lines
From
81bc599911507215aa9faa1077a601880cbd654aMon Sep 17 00:00:00 2001
From: "Masami Hiramatsu (Google)" <[email protected]>
Date: Fri, 13 Sep 2024 21:43:46 +0900
Subject: [PATCH] bpf: Addget_entry_ip()for arm64Add
get_entry_ip()implementation for arm64. This is based on the information inftrace_call_adjust()for arm64.Signed-off-by Masami Hiramatsu (Google) <[email protected]>--- kernel/trace/bpf_trace.c | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+)kernel/trace/bpf_trace.c +64
-1@@ -1066,6 +1066,70 @@ static unsigned long get_entry_ip(unsigned long fentry_ip) fentry_ip -=ENDBR_INSN_SIZE ;return fentry_ip; } +#elif defined (CONFIG_ARM64 ) +#include <asm/insn.h> + +static unsigned long get_entry_ip (unsigned long fentry_ip) +{ +u32 insn; + +/* +* When using patchable-function-entry without pre-function NOPS, ftrace +* entry is the address of the first NOP after the function entry point. +* +* The compiler has either generated: +* +* func+00: func: NOP // To be patched to MOV X9, LR +* func+04: NOP // To be patched to BL <caller> +* +* Or: +* +* func-04: BTI C +* func+00: func: NOP // To be patched to MOV X9, LR +* func+04: NOP // To be patched to BL <caller> +* +* The fentry_ip is the address of `BL <caller>` which is at `func + 4` +* bytes in either case. +*/ +if (!IS_ENABLED (CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS )) +return fentry_ip -AARCH64_INSN_SIZE ; + +/* +* When using patchable-function-entry with pre-function NOPs, BTI is +* a bit different. +* +* func+00: func: NOP // To be patched to MOV X9, LR +* func+04: NOP // To be patched to BL <caller> +* +* Or: +* +* func+00: func: BTI C +* func+04: NOP // To be patched to MOV X9, LR +* func+08: NOP // To be patched to BL <caller> +* +* The fentry_ip is the address of `BL <caller>` which is at either +* `func + 4` or `func + 8` depends on whether there is a BTI. +*/ + +/* If there is no BTI, the func address should be one instruction before. */ +if (!IS_ENABLED (CONFIG_ARM64_BTI_KERNEL )) +return fentry_ip -AARCH64_INSN_SIZE ; + +/* We want to be extra safe in case entry ip is on the page edge, +* but otherwise we need to avoid get_kernel_nofault()'s overhead. +*/ +if ((fentry_ip & ~PAGE_MASK ) <AARCH64_INSN_SIZE *2 ) { +if (get_kernel_nofault (insn, (u32 *)(fentry_ip -AARCH64_INSN_SIZE *2 ))) +return fentry_ip -AARCH64_INSN_SIZE ; + }else { + insn = *(u32 *)(fentry_ip -AARCH64_INSN_SIZE *2 ); + } + +if (aarch64_insn_is_bti (le32_to_cpu ((__le32 )insn))) +return fentry_ip -AARCH64_INSN_SIZE *2 ; + +return fentry_ip -AARCH64_INSN_SIZE ; +}#else #define get_entry_ip (fentry_ip) fentry_ip#endif --2.34.1
--
Masami Hiramatsu (Google) <[email protected]>
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Quoted 110 lines · Masami Hiramatsu
Quoted 73 lines · On Thu, 12 Sep 2024 18:55:40 -0700 Andrii Nakryiko <[email protected]>…
Quoted 50 lines · Masami Hiramatsu
Quoted 46 lines · On Thu, 12 Sep 2024 11:41:17 -0700 Andrii Nakryiko <[email protected]>…
+ BPF ML
Quoted 42 lines · On Thu, Sep 12, 2024 at 8:35 AM <[email protected]> wrote:
Dear patch submitter,
CI has tested the following submission:
Status: FAILURE Name: [v14,00/19] tracing: fprobe: function_graph: Multi-function graph and fprobe on fgraph Patchwork: https://patchwork.kernel.org/project/netdevbpf/list/?series=889822&state=* Matrix: https://github.com/kernel-patches/bpf/actions/runs/10833792984Failed jobs:
test_progs-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791397
test_progs_no_alu32-aarch64-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061791836
test_progs-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757062
test_progs_no_alu32-s390x-gcc: https://github.com/kernel-patches/bpf/actions/runs/10833792984/job/30061757809First
test_progsfailure (test_progs-aarch64-gcc):
#132kprobe_multi_testmod_test
serial_test_kprobe_multi_testmod_test:PASS:load_kallsyms_local 0 nsec
#132/1 kprobe_multi_testmod_test/testmod_attach_api_syms test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsec trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpectedkprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1 #132/2 kprobe_multi_testmod_test/testmod_attach_api_addrs test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api_addrs:PASS:ksym_get_addr_local 0 nsec test_testmod_attach_api:PASS:fentry_raw_skel_load 0 nsec trigger_module_test_read:PASS:testmod_file_open 0 nsec test_testmod_attach_api:PASS:trigger_read 0 nsec kprobe_multi_testmod_check:FAIL:kprobe_test1_result unexpectedkprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test2_result unexpectedkprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kprobe_test3_result unexpectedkprobe_test3_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test1_result unexpectedkretprobe_test1_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test2_result unexpectedkretprobe_test2_result: actual 0 != expected 1 kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpectedkretprobe_test3_result: actual 0 != expected 1Quoted 6 lines
Quoted 3 lines
Seems like this selftest is still broken. Please let me know if you need help with building and running BPF selftests to reproduce this locally.
Thanks, It will be helpful. Also I would like to know, is there any debug mode (like print more debug logs)?
So first of all, the assumption is that you will build a most recent kernel with
Kconfigvalues set fromtools/testing/selftests/bpf/config, so make sure you append that to your kernel config. Then build the kernel, BPF selftests'Makefiletries to find latest built kernel (according to KBUILD_OUTPUT/O/etc).Now to building BPF selftests:
$ cd tools/testing/selftests/bpf $ make -j$(nproc)You'll need decently recent Clang and a few dependent packages. At least elfutils-devel, libzstd-devel, but there might be a few more which I never can remember.
Quoted 7 lines
Once everything is built, you can run the failing test with
$ sudo ./test_progs -t kprobe_multi_testmod_test -vThe source code for user space part for that test is in
prog_tests/kprobe_multi_testmod_test.cand BPF-side is inprogs/kprobe_multi.c.Thanks for the information!
Quoted 9 lines
Taking failing output from the test:
Quoted 1 line
Quoted 1 line
Quoted 1 line
kprobe_multi_testmod_check:FAIL:kretprobe_test3_result unexpected
kretprobe_test3_result: actual 0 != expected 1
kretprobe_test3_resultis a sort of identifier for a test condition, you can find a corresponding line in user space .c file grepping for that:Quoted 6 lines
ASSERT_EQ(skel->bss->kretprobe_testmod_test3_result, 1, "kretprobe_test3_result");Most probably the problem is in:
__u64addr =bpf_get_func_ip(ctx);Yeah, and as I replyed to another thread, the problem is that the ftrace
entry_ipis not symbol ip.We have
ftrace_call_adjust()arch function for reverse direction (symbol ip to ftrace entry ip) but what we need here is the reverse translate function (ftrace entry to symbol)
Quoted 8 lines
The easiest way is to use kallsyms to find it, but this is a bit costful (but it just increase bsearch in several levels). Other possible options are
- Change
bpf_kprobe_multi_addrs_cmp()to accept a range of address. [sym_addr,sym_addr+ offset) returns true,bpf_kprobe_multi_cookie()can find the entry address. The offset depends on arch, but 16 is enough.
This feels quite sloppy, tbh...
Quoted 4 lines
- Change
bpf_kprobe_multi_addrs_cmp()to callftrace_call_adjust()before comparing. This may take a cost but find actual entry address.
too expensive, unnecessary runtime overhead
Quoted 6 lines
- (Cached method) when making
link->addrs, make alink->adj_addrsarray too, where adj_addrs[i] ==ftrace_call_adjust(addrs[i]).Let me try the 3rd one. It may consume more memory but the fastest solution.
I like the third one as well, but I'm not following why we'd need more memory.
We can store adjusted addresses in existing link->addrs, and we'll need to adjust them to originals (potentially expensively) only in bpf_kprobe_multi_link_fill_link_info(). But that's not a hot path, so it doesn't matter.
Quoted 6 lines
Thank you,
Quoted 3 lines
which is then checked as
Quoted 7 lines
Quoted 4 lines
if ((const void *) addr ==
&bpf_testmod_fentry_test3)With your patches this doesn't match anymore.
It actually enables the fprobe on those architectures, so without my series, those test should be skipped.
Ok, cool, still, let's fix the issue.
Quoted 24 lines
Thank you,
Quoted 17 lines
Hopefully the above gives you some pointers, let me know if you run into any problems.
Quoted 12 lines
Thank you,
Quoted 5 lines
Quoted 5 lines
Please note: this email is coming from an unmonitored mailbox. If you have questions or feedback, please reach out to the Meta Kernel CI team at [email protected].
--
Masami Hiramatsu (Google) <[email protected]>--
Masami Hiramatsu (Google) <[email protected]>
Masami Hiramatsu (Google)
maintainerQuoted 6 lines · On Fri, 13 Sep 2024 14:16:47 -0700 Andrii Nakryiko <[email protected]>…
Quoted 4 lines
- Change
bpf_kprobe_multi_addrs_cmp()to callftrace_call_adjust()before comparing. This may take a cost but find actual entry address.too expensive, unnecessary runtime overhead
Indeed.
Quoted 7 lines
Quoted 6 lines
- (Cached method) when making
link->addrs, make alink->adj_addrsarray too, where adj_addrs[i] ==ftrace_call_adjust(addrs[i]).Let me try the 3rd one. It may consume more memory but the fastest solution.
Quoted 6 lines
I like the third one as well, but I'm not following why we'd need more memory.
We can store adjusted addresses in existing
link->addrs, and we'll need to adjust them to originals (potentially expensively) only inbpf_kprobe_multi_link_fill_link_info(). But that's not a hot path, so it doesn't matter.
OK, that works well, but let me check it has any side effects.
BTW, I worry about what the user expects for bpf_get_func_ip().
* u64 bpf_get_func_ip(void *ctx)
* Description
* Get address of the traced function (for tracing and kprobe programs).
*
* When called for kprobe program attached as uprobe it returns
* probe address for both entry and return uprobe.
*
* Return
* Address of the traced function for kprobe.
* 0 for kprobes placed within the function (not at the entry).
* Address of the probe for uprobe and return uprobe.
This seems expecting to get the function address, not ftrace entry address. If user expects it returns actual function entry address, we need to change get_entry_ip()(*) as the patch I sent.
If they can accept the address a bit shifted from the entry address, I think we can change link->addrs.
Jiri, and other BPF users, what you expect and what you want to have? (what the reason to use this API?)
Thank you,
(*) bpf_get_func_ip() seems to read run_ctx->entry_ip which is set by get_entry_ip(fentry_ip).
Quoted 41 lines
Quoted 7 lines
Thank you,
Quoted 3 lines
which is then checked as
Quoted 7 lines
Quoted 4 lines
if ((const void *) addr ==
&bpf_testmod_fentry_test3)With your patches this doesn't match anymore.
It actually enables the fprobe on those architectures, so without my series, those test should be skipped.
Ok, cool, still, let's fix the issue.
Quoted 24 lines
Thank you,
Quoted 17 lines
Hopefully the above gives you some pointers, let me know if you run into any problems.
Quoted 12 lines
Thank you,
Quoted 5 lines
Quoted 5 lines
Please note: this email is coming from an unmonitored mailbox. If you have questions or feedback, please reach out to the Meta Kernel CI team at [email protected].
--
Masami Hiramatsu (Google) <[email protected]>--
Masami Hiramatsu (Google) <[email protected]>
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>