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 * Note that with framepointer enabled, even the leaf functions have the same
24 * prologue and epilogue, therefore we can ignore the LR value in this case.
26 int notrace
unwind_frame(struct stackframe
*frame
)
28 unsigned long high
, low
;
29 unsigned long fp
= frame
->fp
;
31 /* only go to a higher address on the stack */
33 high
= ALIGN(low
, THREAD_SIZE
);
35 /* check current frame pointer is within bounds */
36 if (fp
< low
+ 12 || fp
> high
- 4)
39 /* restore the registers from the stack frame */
40 frame
->fp
= *(unsigned long *)(fp
- 12);
41 frame
->sp
= *(unsigned long *)(fp
- 8);
42 frame
->pc
= *(unsigned long *)(fp
- 4);
48 void notrace
walk_stackframe(struct stackframe
*frame
,
49 int (*fn
)(struct stackframe
*, void *), void *data
)
56 ret
= unwind_frame(frame
);
61 EXPORT_SYMBOL(walk_stackframe
);
63 #ifdef CONFIG_STACKTRACE
64 struct stack_trace_data
{
65 struct stack_trace
*trace
;
66 unsigned long last_pc
;
67 unsigned int no_sched_functions
;
71 static int save_trace(struct stackframe
*frame
, void *d
)
73 struct stack_trace_data
*data
= d
;
74 struct stack_trace
*trace
= data
->trace
;
76 unsigned long addr
= frame
->pc
;
78 if (data
->no_sched_functions
&& in_sched_functions(addr
))
85 trace
->entries
[trace
->nr_entries
++] = addr
;
87 if (trace
->nr_entries
>= trace
->max_entries
)
91 * in_exception_text() is designed to test if the PC is one of
92 * the functions which has an exception stack above it, but
93 * unfortunately what is in frame->pc is the return LR value,
94 * not the saved PC value. So, we need to track the previous
95 * frame PC value when doing this.
98 data
->last_pc
= frame
->pc
;
99 if (!in_exception_text(addr
))
102 regs
= (struct pt_regs
*)frame
->sp
;
104 trace
->entries
[trace
->nr_entries
++] = regs
->ARM_pc
;
106 return trace
->nr_entries
>= trace
->max_entries
;
109 /* This must be noinline to so that our skip calculation works correctly */
110 static noinline
void __save_stack_trace(struct task_struct
*tsk
,
111 struct stack_trace
*trace
, unsigned int nosched
)
113 struct stack_trace_data data
;
114 struct stackframe frame
;
117 data
.last_pc
= ULONG_MAX
;
118 data
.skip
= trace
->skip
;
119 data
.no_sched_functions
= nosched
;
121 if (tsk
!= current
) {
124 * What guarantees do we have here that 'tsk' is not
125 * running on another CPU? For now, ignore it as we
126 * can't guarantee we won't explode.
128 if (trace
->nr_entries
< trace
->max_entries
)
129 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
132 frame
.fp
= thread_saved_fp(tsk
);
133 frame
.sp
= thread_saved_sp(tsk
);
134 frame
.lr
= 0; /* recovered from the stack */
135 frame
.pc
= thread_saved_pc(tsk
);
138 /* We don't want this function nor the caller */
140 frame
.fp
= (unsigned long)__builtin_frame_address(0);
141 frame
.sp
= current_stack_pointer
;
142 frame
.lr
= (unsigned long)__builtin_return_address(0);
143 frame
.pc
= (unsigned long)__save_stack_trace
;
146 walk_stackframe(&frame
, save_trace
, &data
);
147 if (trace
->nr_entries
< trace
->max_entries
)
148 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
151 void save_stack_trace_regs(struct pt_regs
*regs
, struct stack_trace
*trace
)
153 struct stack_trace_data data
;
154 struct stackframe frame
;
157 data
.skip
= trace
->skip
;
158 data
.no_sched_functions
= 0;
160 frame
.fp
= regs
->ARM_fp
;
161 frame
.sp
= regs
->ARM_sp
;
162 frame
.lr
= regs
->ARM_lr
;
163 frame
.pc
= regs
->ARM_pc
;
165 walk_stackframe(&frame
, save_trace
, &data
);
166 if (trace
->nr_entries
< trace
->max_entries
)
167 trace
->entries
[trace
->nr_entries
++] = ULONG_MAX
;
170 void save_stack_trace_tsk(struct task_struct
*tsk
, struct stack_trace
*trace
)
172 __save_stack_trace(tsk
, trace
, 1);
174 EXPORT_SYMBOL(save_stack_trace_tsk
);
176 void save_stack_trace(struct stack_trace
*trace
)
178 __save_stack_trace(current
, trace
, 0);
180 EXPORT_SYMBOL_GPL(save_stack_trace
);