1 // SPDX-License-Identifier: GPL-2.0 2 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> 8 9 #ifdef CONFIG_FRAME_POINTER 10 11 struct stackframe { 12 unsigned long fp; 13 unsigned long ra; 14 }; 15 16 void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, 17 bool (*fn)(unsigned long, void *), void *arg) 18 { 19 unsigned long fp, sp, pc; 20 21 if (regs) { 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"); 28 fp = current_fp; 29 sp = current_sp; 30 pc = (unsigned long)walk_stackframe; 31 } else { 32 /* task blocked in __switch_to */ 33 fp = thread_saved_fp(task); 34 sp = thread_saved_sp(task); 35 pc = thread_saved_lr(task); 36 } 37 38 for (;;) { 39 unsigned long low, high; 40 struct stackframe *frame; 41 42 if (unlikely(!__kernel_text_address(pc) || fn(pc, arg))) 43 break; 44 45 /* Validate frame pointer */ 46 low = sp; 47 high = ALIGN(sp, THREAD_SIZE); 48 if (unlikely(fp < low || fp > high || fp & 0x3)) 49 break; 50 /* Unwind stack frame */ 51 frame = (struct stackframe *)fp; 52 sp = fp; 53 fp = frame->fp; 54 pc = ftrace_graph_ret_addr(current, NULL, frame->ra, 55 (unsigned long *)(fp - 8)); 56 } 57 } 58 59 #else /* !CONFIG_FRAME_POINTER */ 60 61 static void notrace walk_stackframe(struct task_struct *task, 62 struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg) 63 { 64 unsigned long sp, pc; 65 unsigned long *ksp; 66 67 if (regs) { 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"); 72 sp = current_sp; 73 pc = (unsigned long)walk_stackframe; 74 } else { 75 /* task blocked in __switch_to */ 76 sp = thread_saved_sp(task); 77 pc = thread_saved_lr(task); 78 } 79 80 if (unlikely(sp & 0x3)) 81 return; 82 83 ksp = (unsigned long *)sp; 84 while (!kstack_end(ksp)) { 85 if (__kernel_text_address(pc) && unlikely(fn(pc, arg))) 86 break; 87 pc = (*ksp++) - 0x4; 88 } 89 } 90 #endif /* CONFIG_FRAME_POINTER */ 91 92 static bool print_trace_address(unsigned long pc, void *arg) 93 { 94 print_ip_sym((const char *)arg, pc); 95 return false; 96 } 97 98 void show_stack_loglvl(struct task_struct *task, unsigned long *sp, 99 const char *loglvl) 100 { 101 pr_cont("Call Trace:\n"); 102 walk_stackframe(task, NULL, print_trace_address, (void *)loglvl); 103 } 104 105 void show_stack(struct task_struct *task, unsigned long *sp) 106 { 107 pr_cont("Call Trace:\n"); 108 walk_stackframe(task, NULL, print_trace_address, KERN_INFO); 109 } 110 111 static bool save_wchan(unsigned long pc, void *arg) 112 { 113 if (!in_sched_functions(pc)) { 114 unsigned long *p = arg; 115 *p = pc; 116 return true; 117 } 118 return false; 119 } 120 121 unsigned long get_wchan(struct task_struct *task) 122 { 123 unsigned long pc = 0; 124 125 if (likely(task && task != current && task->state != TASK_RUNNING)) 126 walk_stackframe(task, NULL, save_wchan, &pc); 127 return pc; 128 } 129 130 #ifdef CONFIG_STACKTRACE 131 static bool __save_trace(unsigned long pc, void *arg, bool nosched) 132 { 133 struct stack_trace *trace = arg; 134 135 if (unlikely(nosched && in_sched_functions(pc))) 136 return false; 137 if (unlikely(trace->skip > 0)) { 138 trace->skip--; 139 return false; 140 } 141 142 trace->entries[trace->nr_entries++] = pc; 143 return (trace->nr_entries >= trace->max_entries); 144 } 145 146 static bool save_trace(unsigned long pc, void *arg) 147 { 148 return __save_trace(pc, arg, false); 149 } 150 151 /* 152 * Save stack-backtrace addresses into a stack_trace buffer. 153 */ 154 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 155 { 156 walk_stackframe(tsk, NULL, save_trace, trace); 157 } 158 EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 159 160 void save_stack_trace(struct stack_trace *trace) 161 { 162 save_stack_trace_tsk(NULL, trace); 163 } 164 EXPORT_SYMBOL_GPL(save_stack_trace); 165 166 #endif /* CONFIG_STACKTRACE */ 167