xref: /linux/arch/csky/kernel/stacktrace.c (revision aeeb59d692c1bafecde3dfc47bfd0958fc96dfad)
10ea2dc7cSGuo Ren // SPDX-License-Identifier: GPL-2.0
20ea2dc7cSGuo Ren 
30ea2dc7cSGuo Ren #include <linux/sched/debug.h>
40ea2dc7cSGuo Ren #include <linux/sched/task_stack.h>
50ea2dc7cSGuo Ren #include <linux/stacktrace.h>
60ea2dc7cSGuo Ren #include <linux/ftrace.h>
718c07d23SGuo Ren #include <linux/ptrace.h>
818c07d23SGuo Ren 
918c07d23SGuo Ren #ifdef CONFIG_FRAME_POINTER
1018c07d23SGuo Ren 
1118c07d23SGuo Ren struct stackframe {
1218c07d23SGuo Ren 	unsigned long fp;
1318c07d23SGuo Ren 	unsigned long ra;
1418c07d23SGuo Ren };
1518c07d23SGuo Ren 
1618c07d23SGuo Ren void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
1718c07d23SGuo Ren 			     bool (*fn)(unsigned long, void *), void *arg)
1818c07d23SGuo Ren {
1918c07d23SGuo Ren 	unsigned long fp, sp, pc;
2018c07d23SGuo Ren 
2118c07d23SGuo Ren 	if (regs) {
2218c07d23SGuo Ren 		fp = frame_pointer(regs);
2318c07d23SGuo Ren 		sp = user_stack_pointer(regs);
2418c07d23SGuo Ren 		pc = instruction_pointer(regs);
2518c07d23SGuo Ren 	} else if (task == NULL || task == current) {
2618c07d23SGuo Ren 		const register unsigned long current_sp __asm__ ("sp");
2718c07d23SGuo Ren 		const register unsigned long current_fp __asm__ ("r8");
2818c07d23SGuo Ren 		fp = current_fp;
2918c07d23SGuo Ren 		sp = current_sp;
3018c07d23SGuo Ren 		pc = (unsigned long)walk_stackframe;
3118c07d23SGuo Ren 	} else {
3218c07d23SGuo Ren 		/* task blocked in __switch_to */
3318c07d23SGuo Ren 		fp = thread_saved_fp(task);
3418c07d23SGuo Ren 		sp = thread_saved_sp(task);
3518c07d23SGuo Ren 		pc = thread_saved_lr(task);
3618c07d23SGuo Ren 	}
3718c07d23SGuo Ren 
3818c07d23SGuo Ren 	for (;;) {
3918c07d23SGuo Ren 		unsigned long low, high;
4018c07d23SGuo Ren 		struct stackframe *frame;
4118c07d23SGuo Ren 
4218c07d23SGuo Ren 		if (unlikely(!__kernel_text_address(pc) || fn(pc, arg)))
4318c07d23SGuo Ren 			break;
4418c07d23SGuo Ren 
4518c07d23SGuo Ren 		/* Validate frame pointer */
4618c07d23SGuo Ren 		low = sp;
4718c07d23SGuo Ren 		high = ALIGN(sp, THREAD_SIZE);
4818c07d23SGuo Ren 		if (unlikely(fp < low || fp > high || fp & 0x3))
4918c07d23SGuo Ren 			break;
5018c07d23SGuo Ren 		/* Unwind stack frame */
5118c07d23SGuo Ren 		frame = (struct stackframe *)fp;
5218c07d23SGuo Ren 		sp = fp;
5318c07d23SGuo Ren 		fp = frame->fp;
5418c07d23SGuo Ren 		pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
5518c07d23SGuo Ren 					   (unsigned long *)(fp - 8));
5618c07d23SGuo Ren 	}
5718c07d23SGuo Ren }
5818c07d23SGuo Ren 
5918c07d23SGuo Ren #else /* !CONFIG_FRAME_POINTER */
6018c07d23SGuo Ren 
6118c07d23SGuo Ren static void notrace walk_stackframe(struct task_struct *task,
6218c07d23SGuo Ren 	struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg)
6318c07d23SGuo Ren {
6418c07d23SGuo Ren 	unsigned long sp, pc;
6518c07d23SGuo Ren 	unsigned long *ksp;
6618c07d23SGuo Ren 
6718c07d23SGuo Ren 	if (regs) {
6818c07d23SGuo Ren 		sp = user_stack_pointer(regs);
6918c07d23SGuo Ren 		pc = instruction_pointer(regs);
7018c07d23SGuo Ren 	} else if (task == NULL || task == current) {
7118c07d23SGuo Ren 		const register unsigned long current_sp __asm__ ("sp");
7218c07d23SGuo Ren 		sp = current_sp;
7318c07d23SGuo Ren 		pc = (unsigned long)walk_stackframe;
7418c07d23SGuo Ren 	} else {
7518c07d23SGuo Ren 		/* task blocked in __switch_to */
7618c07d23SGuo Ren 		sp = thread_saved_sp(task);
7718c07d23SGuo Ren 		pc = thread_saved_lr(task);
7818c07d23SGuo Ren 	}
7918c07d23SGuo Ren 
8018c07d23SGuo Ren 	if (unlikely(sp & 0x3))
8118c07d23SGuo Ren 		return;
8218c07d23SGuo Ren 
8318c07d23SGuo Ren 	ksp = (unsigned long *)sp;
8418c07d23SGuo Ren 	while (!kstack_end(ksp)) {
8518c07d23SGuo Ren 		if (__kernel_text_address(pc) && unlikely(fn(pc, arg)))
8618c07d23SGuo Ren 			break;
8718c07d23SGuo Ren 		pc = (*ksp++) - 0x4;
8818c07d23SGuo Ren 	}
8918c07d23SGuo Ren }
9018c07d23SGuo Ren #endif /* CONFIG_FRAME_POINTER */
9118c07d23SGuo Ren 
9218c07d23SGuo Ren static bool print_trace_address(unsigned long pc, void *arg)
9318c07d23SGuo Ren {
94*aeeb59d6SDmitry Safonov 	print_ip_sym((const char *)arg, pc);
9518c07d23SGuo Ren 	return false;
9618c07d23SGuo Ren }
9718c07d23SGuo Ren 
98*aeeb59d6SDmitry Safonov void show_stack_loglvl(struct task_struct *task, unsigned long *sp,
99*aeeb59d6SDmitry Safonov 		       const char *loglvl)
100*aeeb59d6SDmitry Safonov {
101*aeeb59d6SDmitry Safonov 	pr_cont("Call Trace:\n");
102*aeeb59d6SDmitry Safonov 	walk_stackframe(task, NULL, print_trace_address, (void *)loglvl);
103*aeeb59d6SDmitry Safonov }
104*aeeb59d6SDmitry Safonov 
10518c07d23SGuo Ren void show_stack(struct task_struct *task, unsigned long *sp)
10618c07d23SGuo Ren {
10718c07d23SGuo Ren 	pr_cont("Call Trace:\n");
108*aeeb59d6SDmitry Safonov 	walk_stackframe(task, NULL, print_trace_address, KERN_INFO);
10918c07d23SGuo Ren }
11018c07d23SGuo Ren 
11118c07d23SGuo Ren static bool save_wchan(unsigned long pc, void *arg)
11218c07d23SGuo Ren {
11318c07d23SGuo Ren 	if (!in_sched_functions(pc)) {
11418c07d23SGuo Ren 		unsigned long *p = arg;
11518c07d23SGuo Ren 		*p = pc;
11618c07d23SGuo Ren 		return true;
11718c07d23SGuo Ren 	}
11818c07d23SGuo Ren 	return false;
11918c07d23SGuo Ren }
12018c07d23SGuo Ren 
12118c07d23SGuo Ren unsigned long get_wchan(struct task_struct *task)
12218c07d23SGuo Ren {
12318c07d23SGuo Ren 	unsigned long pc = 0;
12418c07d23SGuo Ren 
12518c07d23SGuo Ren 	if (likely(task && task != current && task->state != TASK_RUNNING))
12618c07d23SGuo Ren 		walk_stackframe(task, NULL, save_wchan, &pc);
12718c07d23SGuo Ren 	return pc;
12818c07d23SGuo Ren }
12918c07d23SGuo Ren 
13018c07d23SGuo Ren #ifdef CONFIG_STACKTRACE
13118c07d23SGuo Ren static bool __save_trace(unsigned long pc, void *arg, bool nosched)
13218c07d23SGuo Ren {
13318c07d23SGuo Ren 	struct stack_trace *trace = arg;
13418c07d23SGuo Ren 
13518c07d23SGuo Ren 	if (unlikely(nosched && in_sched_functions(pc)))
13618c07d23SGuo Ren 		return false;
13718c07d23SGuo Ren 	if (unlikely(trace->skip > 0)) {
13818c07d23SGuo Ren 		trace->skip--;
13918c07d23SGuo Ren 		return false;
14018c07d23SGuo Ren 	}
14118c07d23SGuo Ren 
14218c07d23SGuo Ren 	trace->entries[trace->nr_entries++] = pc;
14318c07d23SGuo Ren 	return (trace->nr_entries >= trace->max_entries);
14418c07d23SGuo Ren }
14518c07d23SGuo Ren 
14618c07d23SGuo Ren static bool save_trace(unsigned long pc, void *arg)
14718c07d23SGuo Ren {
14818c07d23SGuo Ren 	return __save_trace(pc, arg, false);
14918c07d23SGuo Ren }
15018c07d23SGuo Ren 
15118c07d23SGuo Ren /*
15218c07d23SGuo Ren  * Save stack-backtrace addresses into a stack_trace buffer.
15318c07d23SGuo Ren  */
15418c07d23SGuo Ren void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
15518c07d23SGuo Ren {
15618c07d23SGuo Ren 	walk_stackframe(tsk, NULL, save_trace, trace);
15718c07d23SGuo Ren }
15818c07d23SGuo Ren EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
1590ea2dc7cSGuo Ren 
1600ea2dc7cSGuo Ren void save_stack_trace(struct stack_trace *trace)
1610ea2dc7cSGuo Ren {
16218c07d23SGuo Ren 	save_stack_trace_tsk(NULL, trace);
1630ea2dc7cSGuo Ren }
1640ea2dc7cSGuo Ren EXPORT_SYMBOL_GPL(save_stack_trace);
1650ea2dc7cSGuo Ren 
16618c07d23SGuo Ren #endif /* CONFIG_STACKTRACE */
167