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_fp __asm__ ("r8"); 27 fp = current_fp; 28 sp = current_stack_pointer; 29 pc = (unsigned long)walk_stackframe; 30 } else { 31 /* task blocked in __switch_to */ 32 fp = thread_saved_fp(task); 33 sp = thread_saved_sp(task); 34 pc = thread_saved_lr(task); 35 } 36 37 for (;;) { 38 unsigned long low, high; 39 struct stackframe *frame; 40 41 if (unlikely(!__kernel_text_address(pc) || fn(pc, arg))) 42 break; 43 44 /* Validate frame pointer */ 45 low = sp; 46 high = ALIGN(sp, THREAD_SIZE); 47 if (unlikely(fp < low || fp > high || fp & 0x3)) 48 break; 49 /* Unwind stack frame */ 50 frame = (struct stackframe *)fp; 51 sp = fp; 52 fp = frame->fp; 53 pc = ftrace_graph_ret_addr(current, NULL, frame->ra, 54 (unsigned long *)(fp - 8)); 55 } 56 } 57 58 #else /* !CONFIG_FRAME_POINTER */ 59 60 static void notrace walk_stackframe(struct task_struct *task, 61 struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg) 62 { 63 unsigned long sp, pc; 64 unsigned long *ksp; 65 66 if (regs) { 67 sp = user_stack_pointer(regs); 68 pc = instruction_pointer(regs); 69 } else if (task == NULL || task == current) { 70 sp = current_stack_pointer; 71 pc = (unsigned long)walk_stackframe; 72 } else { 73 /* task blocked in __switch_to */ 74 sp = thread_saved_sp(task); 75 pc = thread_saved_lr(task); 76 } 77 78 if (unlikely(sp & 0x3)) 79 return; 80 81 ksp = (unsigned long *)sp; 82 while (!kstack_end(ksp)) { 83 if (__kernel_text_address(pc) && unlikely(fn(pc, arg))) 84 break; 85 pc = (*ksp++) - 0x4; 86 } 87 } 88 #endif /* CONFIG_FRAME_POINTER */ 89 90 static bool print_trace_address(unsigned long pc, void *arg) 91 { 92 print_ip_sym((const char *)arg, pc); 93 return false; 94 } 95 96 void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl) 97 { 98 pr_cont("Call Trace:\n"); 99 walk_stackframe(task, NULL, print_trace_address, (void *)loglvl); 100 } 101 102 static bool save_wchan(unsigned long pc, void *arg) 103 { 104 if (!in_sched_functions(pc)) { 105 unsigned long *p = arg; 106 *p = pc; 107 return true; 108 } 109 return false; 110 } 111 112 unsigned long __get_wchan(struct task_struct *task) 113 { 114 unsigned long pc = 0; 115 116 walk_stackframe(task, NULL, save_wchan, &pc); 117 return pc; 118 } 119 120 #ifdef CONFIG_STACKTRACE 121 static bool __save_trace(unsigned long pc, void *arg, bool nosched) 122 { 123 struct stack_trace *trace = arg; 124 125 if (unlikely(nosched && in_sched_functions(pc))) 126 return false; 127 if (unlikely(trace->skip > 0)) { 128 trace->skip--; 129 return false; 130 } 131 132 trace->entries[trace->nr_entries++] = pc; 133 return (trace->nr_entries >= trace->max_entries); 134 } 135 136 static bool save_trace(unsigned long pc, void *arg) 137 { 138 return __save_trace(pc, arg, false); 139 } 140 141 /* 142 * Save stack-backtrace addresses into a stack_trace buffer. 143 */ 144 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 145 { 146 walk_stackframe(tsk, NULL, save_trace, trace); 147 } 148 EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 149 150 void save_stack_trace(struct stack_trace *trace) 151 { 152 save_stack_trace_tsk(NULL, trace); 153 } 154 EXPORT_SYMBOL_GPL(save_stack_trace); 155 156 #endif /* CONFIG_STACKTRACE */ 157
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.