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 * Use raw_smp_processor_id() to avoid false-positives from
48 * CONFIG_DEBUG_PREEMPT. get_wchan() calls unwind_frame() on sleeping
49 * task stacks, we can be pre-empted in this case, so
50 * {raw_,}smp_processor_id() may give us the wrong value. Sleeping
51 * tasks can't ever be on an interrupt stack, so regardless of cpu,
52 * the checks will always fail.
54 irq_stack_ptr
= IRQ_STACK_PTR(raw_smp_processor_id());
57 /* irq stacks are not THREAD_SIZE aligned */
58 if (on_irq_stack(frame
->sp
, raw_smp_processor_id()))
61 high
= ALIGN(low
, THREAD_SIZE
) - 0x20;
63 if (fp
< low
|| fp
> high
|| fp
& 0xf)
66 frame
->sp
= fp
+ 0x10;
67 frame
->fp
= *(unsigned long *)(fp
);
68 frame
->pc
= *(unsigned long *)(fp
+ 8);
70 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
71 if (tsk
&& tsk
->ret_stack
&&
72 (frame
->pc
== (unsigned long)return_to_handler
)) {
74 * This is a case where function graph tracer has
75 * modified a return address (LR) in a stack frame
76 * to hook a function return.
77 * So replace it to an original value.
79 frame
->pc
= tsk
->ret_stack
[frame
->graph
--].ret
;
81 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
84 * Check whether we are going to walk through from interrupt stack
86 * If we reach the end of the stack - and its an interrupt stack,
87 * unpack the dummy frame to find the original elr.
89 * Check the frame->fp we read from the bottom of the irq_stack,
90 * and the original task stack pointer are both in current->stack.
92 if (frame
->sp
== irq_stack_ptr
) {
93 struct pt_regs
*irq_args
;
94 unsigned long orig_sp
= IRQ_STACK_TO_TASK_STACK(irq_stack_ptr
);
96 if (object_is_on_stack((void *)orig_sp
) &&
97 object_is_on_stack((void *)frame
->fp
)) {
100 /* orig_sp is the saved pt_regs, find the elr */
101 irq_args
= (struct pt_regs
*)orig_sp
;
102 frame
->pc
= irq_args
->pc
;
105 * This frame has a non-standard format, and we
106 * didn't fix it, because the data looked wrong.
107 * Refuse to output this frame.
116 void notrace
walk_stackframe(struct task_struct
*tsk
, struct stackframe
*frame
,
117 int (*fn
)(struct stackframe
*, void *), void *data
)
124 ret
= unwind_frame(tsk
, frame
);
129 EXPORT_SYMBOL(walk_stackframe
);
131 #ifdef CONFIG_STACKTRACE
132 struct stack_trace_data
{
133 struct stack_trace
*trace
;
134 unsigned int no_sched_functions
;
138 static int save_trace(struct stackframe
*frame
, void *d
)
140 struct stack_trace_data
*data
= d
;
141 struct stack_trace
*trace
= data
->trace
;
142 unsigned long addr
= frame
->pc
;
144 if (data
->no_sched_functions
&& in_sched_functions(addr
))
151 trace
->entries
[trace
->nr_entries
++] = addr
;
153 return trace
->nr_entries
>= trace
->max_entries
;
156 void save_stack_trace_tsk(struct task_struct
*tsk
, struct stack_trace
*trace
)
158 struct stack_trace_data data
;
159 struct stackframe frame
;
162 data
.skip
= trace
->skip
;
164 if (tsk
!= current
) {
165 data
.no_sched_functions
= 1;
166 frame
.fp
= thread_saved_fp(tsk
);
167 frame
.sp
= thread_saved_sp(tsk
);
168 frame
.pc
= thread_saved_pc(tsk
);
170 data
.no_sched_functions
= 0;
171 frame
.fp
= (unsigned long)__builtin_frame_address(0);
172 frame
.sp
= current_stack_pointer
;
173 frame
.pc
= (unsigned long)save_stack_trace_tsk
;
175 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
176 frame
.graph
= tsk
->curr_ret_stack
;
179 walk_stackframe(tsk
, &frame
, save_trace
, &data
);
180 if (trace
->nr_entries
< trace
->max_entries
)
181 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
184 void save_stack_trace(struct stack_trace
*trace
)
186 save_stack_trace_tsk(current
, trace
);
188 EXPORT_SYMBOL_GPL(save_stack_trace
);
This page took 0.045628 seconds and 5 git commands to generate.