1 /* 2 * Copyright (C) 2008 ARM Limited 3 * Copyright (C) 2014 Regents of the University of California 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15 #include <linux/export.h> 16 #include <linux/kallsyms.h> 17 #include <linux/sched.h> 18 #include <linux/sched/debug.h> 19 #include <linux/sched/task_stack.h> 20 #include <linux/stacktrace.h> 21 22 #ifdef CONFIG_FRAME_POINTER 23 24 struct stackframe { 25 unsigned long fp; 26 unsigned long ra; 27 }; 28 29 static void notrace walk_stackframe(struct task_struct *task, 30 struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg) 31 { 32 unsigned long fp, sp, pc; 33 34 if (regs) { 35 fp = GET_FP(regs); 36 sp = GET_USP(regs); 37 pc = GET_IP(regs); 38 } else if (task == NULL || task == current) { 39 const register unsigned long current_sp __asm__ ("sp"); 40 fp = (unsigned long)__builtin_frame_address(0); 41 sp = current_sp; 42 pc = (unsigned long)walk_stackframe; 43 } else { 44 /* task blocked in __switch_to */ 45 fp = task->thread.s[0]; 46 sp = task->thread.sp; 47 pc = task->thread.ra; 48 } 49 50 for (;;) { 51 unsigned long low, high; 52 struct stackframe *frame; 53 54 if (unlikely(!__kernel_text_address(pc) || fn(pc, arg))) 55 break; 56 57 /* Validate frame pointer */ 58 low = sp + sizeof(struct stackframe); 59 high = ALIGN(sp, THREAD_SIZE); 60 if (unlikely(fp < low || fp > high || fp & 0x7)) 61 break; 62 /* Unwind stack frame */ 63 frame = (struct stackframe *)fp - 1; 64 sp = fp; 65 fp = frame->fp; 66 pc = frame->ra - 0x4; 67 } 68 } 69 70 #else /* !CONFIG_FRAME_POINTER */ 71 72 static void notrace walk_stackframe(struct task_struct *task, 73 struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg) 74 { 75 unsigned long sp, pc; 76 unsigned long *ksp; 77 78 if (regs) { 79 sp = GET_USP(regs); 80 pc = GET_IP(regs); 81 } else if (task == NULL || task == current) { 82 const register unsigned long current_sp __asm__ ("sp"); 83 sp = current_sp; 84 pc = (unsigned long)walk_stackframe; 85 } else { 86 /* task blocked in __switch_to */ 87 sp = task->thread.sp; 88 pc = task->thread.ra; 89 } 90 91 if (unlikely(sp & 0x7)) 92 return; 93 94 ksp = (unsigned long *)sp; 95 while (!kstack_end(ksp)) { 96 if (__kernel_text_address(pc) && unlikely(fn(pc, arg))) 97 break; 98 pc = (*ksp++) - 0x4; 99 } 100 } 101 102 #endif /* CONFIG_FRAME_POINTER */ 103 104 105 static bool print_trace_address(unsigned long pc, void *arg) 106 { 107 print_ip_sym(pc); 108 return false; 109 } 110 111 void show_stack(struct task_struct *task, unsigned long *sp) 112 { 113 pr_cont("Call Trace:\n"); 114 walk_stackframe(task, NULL, print_trace_address, NULL); 115 } 116 117 118 static bool save_wchan(unsigned long pc, void *arg) 119 { 120 if (!in_sched_functions(pc)) { 121 unsigned long *p = arg; 122 *p = pc; 123 return true; 124 } 125 return false; 126 } 127 128 unsigned long get_wchan(struct task_struct *task) 129 { 130 unsigned long pc = 0; 131 132 if (likely(task && task != current && task->state != TASK_RUNNING)) 133 walk_stackframe(task, NULL, save_wchan, &pc); 134 return pc; 135 } 136 137 138 #ifdef CONFIG_STACKTRACE 139 140 static bool __save_trace(unsigned long pc, void *arg, bool nosched) 141 { 142 struct stack_trace *trace = arg; 143 144 if (unlikely(nosched && in_sched_functions(pc))) 145 return false; 146 if (unlikely(trace->skip > 0)) { 147 trace->skip--; 148 return false; 149 } 150 151 trace->entries[trace->nr_entries++] = pc; 152 return (trace->nr_entries >= trace->max_entries); 153 } 154 155 static bool save_trace(unsigned long pc, void *arg) 156 { 157 return __save_trace(pc, arg, false); 158 } 159 160 /* 161 * Save stack-backtrace addresses into a stack_trace buffer. 162 */ 163 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 164 { 165 walk_stackframe(tsk, NULL, save_trace, trace); 166 if (trace->nr_entries < trace->max_entries) 167 trace->entries[trace->nr_entries++] = ULONG_MAX; 168 } 169 EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 170 171 void save_stack_trace(struct stack_trace *trace) 172 { 173 save_stack_trace_tsk(NULL, trace); 174 } 175 EXPORT_SYMBOL_GPL(save_stack_trace); 176 177 #endif /* CONFIG_STACKTRACE */ 178