1 #include <linux/export.h>
2 #include <linux/sched.h>
3 #include <linux/sched/debug.h>
4 #include <linux/stacktrace.h>
6 #include <asm/stacktrace.h>
9 #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
11 * Unwind the current stack frame and store the new register values in the
12 * structure passed as argument. Unwinding is equivalent to a function return,
13 * hence the new PC value rather than LR should be used for backtrace.
15 * With framepointer enabled, a simple function prologue looks like this:
17 * stmdb sp!, {fp, ip, lr, pc}
20 * A simple function epilogue looks like this:
21 * ldm sp, {fp, sp, pc}
23 * When compiled with clang, pc and sp are not pushed. A simple function
24 * prologue looks like this when built with clang:
30 * A simple function epilogue looks like this when built with clang:
36 * Note that with framepointer enabled, even the leaf functions have the same
37 * prologue and epilogue, therefore we can ignore the LR value in this case.
39 int notrace
unwind_frame(struct stackframe
*frame
)
41 unsigned long high
, low
;
42 unsigned long fp
= frame
->fp
;
44 /* only go to a higher address on the stack */
46 high
= ALIGN(low
, THREAD_SIZE
);
48 #ifdef CONFIG_CC_IS_CLANG
49 /* check current frame pointer is within bounds */
50 if (fp
< low
+ 4 || fp
> high
- 4)
53 frame
->sp
= frame
->fp
;
54 frame
->fp
= *(unsigned long *)(fp
);
55 frame
->pc
= frame
->lr
;
56 frame
->lr
= *(unsigned long *)(fp
+ 4);
58 /* check current frame pointer is within bounds */
59 if (fp
< low
+ 12 || fp
> high
- 4)
62 /* restore the registers from the stack frame */
63 frame
->fp
= *(unsigned long *)(fp
- 12);
64 frame
->sp
= *(unsigned long *)(fp
- 8);
65 frame
->pc
= *(unsigned long *)(fp
- 4);
72 void notrace
walk_stackframe(struct stackframe
*frame
,
73 int (*fn
)(struct stackframe
*, void *), void *data
)
80 ret
= unwind_frame(frame
);
85 EXPORT_SYMBOL(walk_stackframe
);
87 #ifdef CONFIG_STACKTRACE
88 struct stack_trace_data
{
89 struct stack_trace
*trace
;
90 unsigned long last_pc
;
91 unsigned int no_sched_functions
;
95 static int save_trace(struct stackframe
*frame
, void *d
)
97 struct stack_trace_data
*data
= d
;
98 struct stack_trace
*trace
= data
->trace
;
100 unsigned long addr
= frame
->pc
;
102 if (data
->no_sched_functions
&& in_sched_functions(addr
))
109 trace
->entries
[trace
->nr_entries
++] = addr
;
111 if (trace
->nr_entries
>= trace
->max_entries
)
115 * in_exception_text() is designed to test if the PC is one of
116 * the functions which has an exception stack above it, but
117 * unfortunately what is in frame->pc is the return LR value,
118 * not the saved PC value. So, we need to track the previous
119 * frame PC value when doing this.
121 addr
= data
->last_pc
;
122 data
->last_pc
= frame
->pc
;
123 if (!in_exception_text(addr
))
126 regs
= (struct pt_regs
*)frame
->sp
;
128 trace
->entries
[trace
->nr_entries
++] = regs
->ARM_pc
;
130 return trace
->nr_entries
>= trace
->max_entries
;
133 /* This must be noinline to so that our skip calculation works correctly */
134 static noinline
void __save_stack_trace(struct task_struct
*tsk
,
135 struct stack_trace
*trace
, unsigned int nosched
)
137 struct stack_trace_data data
;
138 struct stackframe frame
;
141 data
.last_pc
= ULONG_MAX
;
142 data
.skip
= trace
->skip
;
143 data
.no_sched_functions
= nosched
;
145 if (tsk
!= current
) {
148 * What guarantees do we have here that 'tsk' is not
149 * running on another CPU? For now, ignore it as we
150 * can't guarantee we won't explode.
152 if (trace
->nr_entries
< trace
->max_entries
)
153 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
156 frame
.fp
= thread_saved_fp(tsk
);
157 frame
.sp
= thread_saved_sp(tsk
);
158 frame
.lr
= 0; /* recovered from the stack */
159 frame
.pc
= thread_saved_pc(tsk
);
162 /* We don't want this function nor the caller */
164 frame
.fp
= (unsigned long)__builtin_frame_address(0);
165 frame
.sp
= current_stack_pointer
;
166 frame
.lr
= (unsigned long)__builtin_return_address(0);
167 frame
.pc
= (unsigned long)__save_stack_trace
;
170 walk_stackframe(&frame
, save_trace
, &data
);
171 if (trace
->nr_entries
< trace
->max_entries
)
172 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
175 void save_stack_trace_regs(struct pt_regs
*regs
, struct stack_trace
*trace
)
177 struct stack_trace_data data
;
178 struct stackframe frame
;
181 data
.skip
= trace
->skip
;
182 data
.no_sched_functions
= 0;
184 frame
.fp
= regs
->ARM_fp
;
185 frame
.sp
= regs
->ARM_sp
;
186 frame
.lr
= regs
->ARM_lr
;
187 frame
.pc
= regs
->ARM_pc
;
189 walk_stackframe(&frame
, save_trace
, &data
);
190 if (trace
->nr_entries
< trace
->max_entries
)
191 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
194 void save_stack_trace_tsk(struct task_struct
*tsk
, struct stack_trace
*trace
)
196 __save_stack_trace(tsk
, trace
, 1);
198 EXPORT_SYMBOL(save_stack_trace_tsk
);
200 void save_stack_trace(struct stack_trace
*trace
)
202 __save_stack_trace(current
, trace
, 0);
204 EXPORT_SYMBOL_GPL(save_stack_trace
);