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/stacktrace.h>
25 #include <asm/stacktrace.h>
28 * AArch64 PCS assigns the frame pointer to x29.
30 * A simple function prologue looks like this:
35 * A simple function epilogue looks like this:
40 int notrace
unwind_frame(struct task_struct
*tsk
, struct stackframe
*frame
)
42 unsigned long high
, low
;
43 unsigned long fp
= frame
->fp
;
44 unsigned long irq_stack_ptr
;
50 * Switching between stacks is valid when tracing current and in
51 * non-preemptible context.
53 if (tsk
== current
&& !preemptible())
54 irq_stack_ptr
= IRQ_STACK_PTR(smp_processor_id());
59 /* irq stacks are not THREAD_SIZE aligned */
60 if (on_irq_stack(frame
->sp
, raw_smp_processor_id()))
63 high
= ALIGN(low
, THREAD_SIZE
) - 0x20;
65 if (fp
< low
|| fp
> high
|| fp
& 0xf)
68 frame
->sp
= fp
+ 0x10;
69 frame
->fp
= READ_ONCE_NOCHECK(*(unsigned long *)(fp
));
70 frame
->pc
= READ_ONCE_NOCHECK(*(unsigned long *)(fp
+ 8));
72 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
74 (frame
->pc
== (unsigned long)return_to_handler
)) {
76 * This is a case where function graph tracer has
77 * modified a return address (LR) in a stack frame
78 * to hook a function return.
79 * So replace it to an original value.
81 frame
->pc
= tsk
->ret_stack
[frame
->graph
--].ret
;
83 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
86 * Check whether we are going to walk through from interrupt stack
88 * If we reach the end of the stack - and its an interrupt stack,
89 * unpack the dummy frame to find the original elr.
91 * Check the frame->fp we read from the bottom of the irq_stack,
92 * and the original task stack pointer are both in current->stack.
94 if (frame
->sp
== irq_stack_ptr
) {
95 struct pt_regs
*irq_args
;
96 unsigned long orig_sp
= IRQ_STACK_TO_TASK_STACK(irq_stack_ptr
);
98 if (object_is_on_stack((void *)orig_sp
) &&
99 object_is_on_stack((void *)frame
->fp
)) {
102 /* orig_sp is the saved pt_regs, find the elr */
103 irq_args
= (struct pt_regs
*)orig_sp
;
104 frame
->pc
= irq_args
->pc
;
107 * This frame has a non-standard format, and we
108 * didn't fix it, because the data looked wrong.
109 * Refuse to output this frame.
118 void notrace
walk_stackframe(struct task_struct
*tsk
, struct stackframe
*frame
,
119 int (*fn
)(struct stackframe
*, void *), void *data
)
126 ret
= unwind_frame(tsk
, frame
);
131 EXPORT_SYMBOL(walk_stackframe
);
133 #ifdef CONFIG_STACKTRACE
134 struct stack_trace_data
{
135 struct stack_trace
*trace
;
136 unsigned int no_sched_functions
;
140 static int save_trace(struct stackframe
*frame
, void *d
)
142 struct stack_trace_data
*data
= d
;
143 struct stack_trace
*trace
= data
->trace
;
144 unsigned long addr
= frame
->pc
;
146 if (data
->no_sched_functions
&& in_sched_functions(addr
))
153 trace
->entries
[trace
->nr_entries
++] = addr
;
155 return trace
->nr_entries
>= trace
->max_entries
;
158 void save_stack_trace_regs(struct pt_regs
*regs
, struct stack_trace
*trace
)
160 struct stack_trace_data data
;
161 struct stackframe frame
;
164 data
.skip
= trace
->skip
;
165 data
.no_sched_functions
= 0;
167 frame
.fp
= regs
->regs
[29];
170 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
171 frame
.graph
= current
->curr_ret_stack
;
174 walk_stackframe(current
, &frame
, save_trace
, &data
);
175 if (trace
->nr_entries
< trace
->max_entries
)
176 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
179 void save_stack_trace_tsk(struct task_struct
*tsk
, struct stack_trace
*trace
)
181 struct stack_trace_data data
;
182 struct stackframe frame
;
185 data
.skip
= trace
->skip
;
187 if (tsk
!= current
) {
188 data
.no_sched_functions
= 1;
189 frame
.fp
= thread_saved_fp(tsk
);
190 frame
.sp
= thread_saved_sp(tsk
);
191 frame
.pc
= thread_saved_pc(tsk
);
193 data
.no_sched_functions
= 0;
194 frame
.fp
= (unsigned long)__builtin_frame_address(0);
195 frame
.sp
= current_stack_pointer
;
196 frame
.pc
= (unsigned long)save_stack_trace_tsk
;
198 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
199 frame
.graph
= tsk
->curr_ret_stack
;
202 walk_stackframe(tsk
, &frame
, save_trace
, &data
);
203 if (trace
->nr_entries
< trace
->max_entries
)
204 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
207 void save_stack_trace(struct stack_trace
*trace
)
209 save_stack_trace_tsk(current
, trace
);
211 EXPORT_SYMBOL_GPL(save_stack_trace
);