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
;
47 * Switching between stacks is valid when tracing current and in
48 * non-preemptible context.
50 if (tsk
== current
&& !preemptible())
51 irq_stack_ptr
= IRQ_STACK_PTR(smp_processor_id());
56 /* irq stacks are not THREAD_SIZE aligned */
57 if (on_irq_stack(frame
->sp
, raw_smp_processor_id()))
60 high
= ALIGN(low
, THREAD_SIZE
) - 0x20;
62 if (fp
< low
|| fp
> high
|| fp
& 0xf)
65 frame
->sp
= fp
+ 0x10;
66 frame
->fp
= READ_ONCE_NOCHECK(*(unsigned long *)(fp
));
67 frame
->pc
= READ_ONCE_NOCHECK(*(unsigned long *)(fp
+ 8));
69 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
70 if (tsk
&& tsk
->ret_stack
&&
71 (frame
->pc
== (unsigned long)return_to_handler
)) {
73 * This is a case where function graph tracer has
74 * modified a return address (LR) in a stack frame
75 * to hook a function return.
76 * So replace it to an original value.
78 frame
->pc
= tsk
->ret_stack
[frame
->graph
--].ret
;
80 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
83 * Check whether we are going to walk through from interrupt stack
85 * If we reach the end of the stack - and its an interrupt stack,
86 * unpack the dummy frame to find the original elr.
88 * Check the frame->fp we read from the bottom of the irq_stack,
89 * and the original task stack pointer are both in current->stack.
91 if (frame
->sp
== irq_stack_ptr
) {
92 struct pt_regs
*irq_args
;
93 unsigned long orig_sp
= IRQ_STACK_TO_TASK_STACK(irq_stack_ptr
);
95 if (object_is_on_stack((void *)orig_sp
) &&
96 object_is_on_stack((void *)frame
->fp
)) {
99 /* orig_sp is the saved pt_regs, find the elr */
100 irq_args
= (struct pt_regs
*)orig_sp
;
101 frame
->pc
= irq_args
->pc
;
104 * This frame has a non-standard format, and we
105 * didn't fix it, because the data looked wrong.
106 * Refuse to output this frame.
115 void notrace
walk_stackframe(struct task_struct
*tsk
, struct stackframe
*frame
,
116 int (*fn
)(struct stackframe
*, void *), void *data
)
123 ret
= unwind_frame(tsk
, frame
);
128 EXPORT_SYMBOL(walk_stackframe
);
130 #ifdef CONFIG_STACKTRACE
131 struct stack_trace_data
{
132 struct stack_trace
*trace
;
133 unsigned int no_sched_functions
;
137 static int save_trace(struct stackframe
*frame
, void *d
)
139 struct stack_trace_data
*data
= d
;
140 struct stack_trace
*trace
= data
->trace
;
141 unsigned long addr
= frame
->pc
;
143 if (data
->no_sched_functions
&& in_sched_functions(addr
))
150 trace
->entries
[trace
->nr_entries
++] = addr
;
152 return trace
->nr_entries
>= trace
->max_entries
;
155 void save_stack_trace_tsk(struct task_struct
*tsk
, struct stack_trace
*trace
)
157 struct stack_trace_data data
;
158 struct stackframe frame
;
161 data
.skip
= trace
->skip
;
163 if (tsk
!= current
) {
164 data
.no_sched_functions
= 1;
165 frame
.fp
= thread_saved_fp(tsk
);
166 frame
.sp
= thread_saved_sp(tsk
);
167 frame
.pc
= thread_saved_pc(tsk
);
169 data
.no_sched_functions
= 0;
170 frame
.fp
= (unsigned long)__builtin_frame_address(0);
171 frame
.sp
= current_stack_pointer
;
172 frame
.pc
= (unsigned long)save_stack_trace_tsk
;
174 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
175 frame
.graph
= tsk
->curr_ret_stack
;
178 walk_stackframe(tsk
, &frame
, save_trace
, &data
);
179 if (trace
->nr_entries
< trace
->max_entries
)
180 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
183 void save_stack_trace(struct stack_trace
*trace
)
185 save_stack_trace_tsk(current
, trace
);
187 EXPORT_SYMBOL_GPL(save_stack_trace
);
This page took 0.035813 seconds and 5 git commands to generate.