mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-15 13:15:57 +00:00
function_graph: Handle tail calls for stack unwinding
For the tail-call, there would be 2 or more ftrace_ret_stacks on the ret_stack, which records "return_to_handler" as the return address except for the last one. But on the real stack, there should be 1 entry because tail-call reuses the return address on the stack and jump to the next function. In ftrace_graph_ret_addr() that is used for stack unwinding, skip tail calls as a real stack unwinder would do. Link: https://lore.kernel.org/linux-trace-kernel/171509096221.162236.8806372072523195752.stgit@devnote2 Link: https://lore.kernel.org/linux-trace-kernel/20240603190821.717065217@goodmis.org Cc: Mark Rutland <mark.rutland@arm.com> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com> Cc: Florent Revest <revest@chromium.org> Cc: Martin KaFai Lau <martin.lau@linux.dev> Cc: bpf <bpf@vger.kernel.org> Cc: Sven Schnelle <svens@linux.ibm.com> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Alan Maguire <alan.maguire@oracle.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Guo Ren <guoren@kernel.org> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
This commit is contained in:
parent
7aa1eaef9f
commit
375bb57292
@ -594,16 +594,26 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
|
||||
unsigned long ret, unsigned long *retp)
|
||||
{
|
||||
struct ftrace_ret_stack *ret_stack;
|
||||
unsigned long return_handler = (unsigned long)dereference_kernel_function_descriptor(return_to_handler);
|
||||
int i = task->curr_ret_stack;
|
||||
|
||||
if (ret != (unsigned long)dereference_kernel_function_descriptor(return_to_handler))
|
||||
if (ret != return_handler)
|
||||
return ret;
|
||||
|
||||
while (i > 0) {
|
||||
ret_stack = get_ret_stack(current, i, &i);
|
||||
if (!ret_stack)
|
||||
break;
|
||||
if (ret_stack->retp == retp)
|
||||
/*
|
||||
* For the tail-call, there would be 2 or more ftrace_ret_stacks on
|
||||
* the ret_stack, which records "return_to_handler" as the return
|
||||
* address except for the last one.
|
||||
* But on the real stack, there should be 1 entry because tail-call
|
||||
* reuses the return address on the stack and jump to the next function.
|
||||
* Thus we will continue to find real return address.
|
||||
*/
|
||||
if (ret_stack->retp == retp &&
|
||||
ret_stack->ret != return_handler)
|
||||
return ret_stack->ret;
|
||||
}
|
||||
|
||||
@ -614,10 +624,11 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
|
||||
unsigned long ret, unsigned long *retp)
|
||||
{
|
||||
struct ftrace_ret_stack *ret_stack;
|
||||
unsigned long return_handler = (unsigned long)dereference_kernel_function_descriptor(return_to_handler);
|
||||
int offset = task->curr_ret_stack;
|
||||
int i;
|
||||
|
||||
if (ret != (unsigned long)dereference_kernel_function_descriptor(return_to_handler))
|
||||
if (ret != return_handler)
|
||||
return ret;
|
||||
|
||||
if (!idx)
|
||||
@ -626,6 +637,8 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
|
||||
i = *idx;
|
||||
do {
|
||||
ret_stack = get_ret_stack(task, offset, &offset);
|
||||
if (ret_stack && ret_stack->ret == return_handler)
|
||||
continue;
|
||||
i--;
|
||||
} while (i >= 0 && ret_stack);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user