2 * Stack tracing support
4 * Copyright (C) 2012 ARM Ltd.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include <linux/kernel.h>
19 #include <linux/export.h>
20 #include <linux/ftrace.h>
21 #include <linux/sched.h>
22 #include <linux/sched/debug.h>
23 #include <linux/sched/task_stack.h>
24 #include <linux/stacktrace.h>
27 #include <asm/stack_pointer.h>
28 #include <asm/stacktrace.h>
31 * AArch64 PCS assigns the frame pointer to x29.
33 * A simple function prologue looks like this:
38 * A simple function epilogue looks like this:
43 int notrace
unwind_frame(struct task_struct
*tsk
, struct stackframe
*frame
)
45 unsigned long high
, low
;
46 unsigned long fp
= frame
->fp
;
47 unsigned long irq_stack_ptr
;
53 * Switching between stacks is valid when tracing current and in
54 * non-preemptible context.
56 if (tsk
== current
&& !preemptible())
57 irq_stack_ptr
= IRQ_STACK_PTR(smp_processor_id());
62 /* irq stacks are not THREAD_SIZE aligned */
63 if (on_irq_stack(frame
->sp
, raw_smp_processor_id()))
66 high
= ALIGN(low
, THREAD_SIZE
) - 0x20;
68 if (fp
< low
|| fp
> high
|| fp
& 0xf)
71 frame
->sp
= fp
+ 0x10;
72 frame
->fp
= READ_ONCE_NOCHECK(*(unsigned long *)(fp
));
73 frame
->pc
= READ_ONCE_NOCHECK(*(unsigned long *)(fp
+ 8));
75 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
77 (frame
->pc
== (unsigned long)return_to_handler
)) {
79 * This is a case where function graph tracer has
80 * modified a return address (LR) in a stack frame
81 * to hook a function return.
82 * So replace it to an original value.
84 frame
->pc
= tsk
->ret_stack
[frame
->graph
--].ret
;
86 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
89 * Check whether we are going to walk through from interrupt stack
91 * If we reach the end of the stack - and its an interrupt stack,
92 * unpack the dummy frame to find the original elr.
94 * Check the frame->fp we read from the bottom of the irq_stack,
95 * and the original task stack pointer are both in current->stack.
97 if (frame
->sp
== irq_stack_ptr
) {
98 struct pt_regs
*irq_args
;
99 unsigned long orig_sp
= IRQ_STACK_TO_TASK_STACK(irq_stack_ptr
);
101 if (object_is_on_stack((void *)orig_sp
) &&
102 object_is_on_stack((void *)frame
->fp
)) {
105 /* orig_sp is the saved pt_regs, find the elr */
106 irq_args
= (struct pt_regs
*)orig_sp
;
107 frame
->pc
= irq_args
->pc
;
110 * This frame has a non-standard format, and we
111 * didn't fix it, because the data looked wrong.
112 * Refuse to output this frame.
121 void notrace
walk_stackframe(struct task_struct
*tsk
, struct stackframe
*frame
,
122 int (*fn
)(struct stackframe
*, void *), void *data
)
129 ret
= unwind_frame(tsk
, frame
);
135 #ifdef CONFIG_STACKTRACE
136 struct stack_trace_data
{
137 struct stack_trace
*trace
;
138 unsigned int no_sched_functions
;
142 static int save_trace(struct stackframe
*frame
, void *d
)
144 struct stack_trace_data
*data
= d
;
145 struct stack_trace
*trace
= data
->trace
;
146 unsigned long addr
= frame
->pc
;
148 if (data
->no_sched_functions
&& in_sched_functions(addr
))
155 trace
->entries
[trace
->nr_entries
++] = addr
;
157 return trace
->nr_entries
>= trace
->max_entries
;
160 void save_stack_trace_regs(struct pt_regs
*regs
, struct stack_trace
*trace
)
162 struct stack_trace_data data
;
163 struct stackframe frame
;
166 data
.skip
= trace
->skip
;
167 data
.no_sched_functions
= 0;
169 frame
.fp
= regs
->regs
[29];
172 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
173 frame
.graph
= current
->curr_ret_stack
;
176 walk_stackframe(current
, &frame
, save_trace
, &data
);
177 if (trace
->nr_entries
< trace
->max_entries
)
178 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
181 void save_stack_trace_tsk(struct task_struct
*tsk
, struct stack_trace
*trace
)
183 struct stack_trace_data data
;
184 struct stackframe frame
;
186 if (!try_get_task_stack(tsk
))
190 data
.skip
= trace
->skip
;
192 if (tsk
!= current
) {
193 data
.no_sched_functions
= 1;
194 frame
.fp
= thread_saved_fp(tsk
);
195 frame
.sp
= thread_saved_sp(tsk
);
196 frame
.pc
= thread_saved_pc(tsk
);
198 data
.no_sched_functions
= 0;
199 frame
.fp
= (unsigned long)__builtin_frame_address(0);
200 frame
.sp
= current_stack_pointer
;
201 frame
.pc
= (unsigned long)save_stack_trace_tsk
;
203 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
204 frame
.graph
= tsk
->curr_ret_stack
;
207 walk_stackframe(tsk
, &frame
, save_trace
, &data
);
208 if (trace
->nr_entries
< trace
->max_entries
)
209 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
214 void save_stack_trace(struct stack_trace
*trace
)
216 save_stack_trace_tsk(current
, trace
);
218 EXPORT_SYMBOL_GPL(save_stack_trace
);