1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/sched/debug.h>
4 #include <linux/sched/task_stack.h>
5 #include <linux/stacktrace.h>
6 #include <linux/ftrace.h>
7 #include <linux/ptrace.h>
9 #ifdef CONFIG_FRAME_POINTER
16 void notrace
walk_stackframe(struct task_struct
*task
, struct pt_regs
*regs
,
17 bool (*fn
)(unsigned long, void *), void *arg
)
19 unsigned long fp
, sp
, pc
;
22 fp
= frame_pointer(regs
);
23 sp
= user_stack_pointer(regs
);
24 pc
= instruction_pointer(regs
);
25 } else if (task
== NULL
|| task
== current
) {
26 const register unsigned long current_sp
__asm__ ("sp");
27 const register unsigned long current_fp
__asm__ ("r8");
30 pc
= (unsigned long)walk_stackframe
;
32 /* task blocked in __switch_to */
33 fp
= thread_saved_fp(task
);
34 sp
= thread_saved_sp(task
);
35 pc
= thread_saved_lr(task
);
39 unsigned long low
, high
;
40 struct stackframe
*frame
;
42 if (unlikely(!__kernel_text_address(pc
) || fn(pc
, arg
)))
45 /* Validate frame pointer */
47 high
= ALIGN(sp
, THREAD_SIZE
);
48 if (unlikely(fp
< low
|| fp
> high
|| fp
& 0x3))
50 /* Unwind stack frame */
51 frame
= (struct stackframe
*)fp
;
54 pc
= ftrace_graph_ret_addr(current
, NULL
, frame
->ra
,
55 (unsigned long *)(fp
- 8));
59 #else /* !CONFIG_FRAME_POINTER */
61 static void notrace
walk_stackframe(struct task_struct
*task
,
62 struct pt_regs
*regs
, bool (*fn
)(unsigned long, void *), void *arg
)
68 sp
= user_stack_pointer(regs
);
69 pc
= instruction_pointer(regs
);
70 } else if (task
== NULL
|| task
== current
) {
71 const register unsigned long current_sp
__asm__ ("sp");
73 pc
= (unsigned long)walk_stackframe
;
75 /* task blocked in __switch_to */
76 sp
= thread_saved_sp(task
);
77 pc
= thread_saved_lr(task
);
80 if (unlikely(sp
& 0x3))
83 ksp
= (unsigned long *)sp
;
84 while (!kstack_end(ksp
)) {
85 if (__kernel_text_address(pc
) && unlikely(fn(pc
, arg
)))
90 #endif /* CONFIG_FRAME_POINTER */
92 static bool print_trace_address(unsigned long pc
, void *arg
)
94 print_ip_sym((const char *)arg
, pc
);
98 void show_stack(struct task_struct
*task
, unsigned long *sp
, const char *loglvl
)
100 pr_cont("Call Trace:\n");
101 walk_stackframe(task
, NULL
, print_trace_address
, (void *)loglvl
);
104 static bool save_wchan(unsigned long pc
, void *arg
)
106 if (!in_sched_functions(pc
)) {
107 unsigned long *p
= arg
;
114 unsigned long get_wchan(struct task_struct
*task
)
116 unsigned long pc
= 0;
118 if (likely(task
&& task
!= current
&& task
->state
!= TASK_RUNNING
))
119 walk_stackframe(task
, NULL
, save_wchan
, &pc
);
123 #ifdef CONFIG_STACKTRACE
124 static bool __save_trace(unsigned long pc
, void *arg
, bool nosched
)
126 struct stack_trace
*trace
= arg
;
128 if (unlikely(nosched
&& in_sched_functions(pc
)))
130 if (unlikely(trace
->skip
> 0)) {
135 trace
->entries
[trace
->nr_entries
++] = pc
;
136 return (trace
->nr_entries
>= trace
->max_entries
);
139 static bool save_trace(unsigned long pc
, void *arg
)
141 return __save_trace(pc
, arg
, false);
145 * Save stack-backtrace addresses into a stack_trace buffer.
147 void save_stack_trace_tsk(struct task_struct
*tsk
, struct stack_trace
*trace
)
149 walk_stackframe(tsk
, NULL
, save_trace
, trace
);
151 EXPORT_SYMBOL_GPL(save_stack_trace_tsk
);
153 void save_stack_trace(struct stack_trace
*trace
)
155 save_stack_trace_tsk(NULL
, trace
);
157 EXPORT_SYMBOL_GPL(save_stack_trace
);
159 #endif /* CONFIG_STACKTRACE */