1 /* 2 * Stack trace management functions 3 * 4 * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp> 5 */ 6 #include <linux/sched.h> 7 #include <linux/stacktrace.h> 8 #include <linux/export.h> 9 #include <asm/stacktrace.h> 10 11 /* 12 * Save stack-backtrace addresses into a stack_trace buffer: 13 */ 14 static void save_raw_context_stack(struct stack_trace *trace, 15 unsigned long reg29) 16 { 17 unsigned long *sp = (unsigned long *)reg29; 18 unsigned long addr; 19 20 while (!kstack_end(sp)) { 21 addr = *sp++; 22 if (__kernel_text_address(addr)) { 23 if (trace->skip > 0) 24 trace->skip--; 25 else 26 trace->entries[trace->nr_entries++] = addr; 27 if (trace->nr_entries >= trace->max_entries) 28 break; 29 } 30 } 31 } 32 33 static void save_context_stack(struct stack_trace *trace, 34 struct task_struct *tsk, struct pt_regs *regs) 35 { 36 unsigned long sp = regs->regs[29]; 37 #ifdef CONFIG_KALLSYMS 38 unsigned long ra = regs->regs[31]; 39 unsigned long pc = regs->cp0_epc; 40 41 if (raw_show_trace || !__kernel_text_address(pc)) { 42 unsigned long stack_page = 43 (unsigned long)task_stack_page(tsk); 44 if (stack_page && sp >= stack_page && 45 sp <= stack_page + THREAD_SIZE - 32) 46 save_raw_context_stack(trace, sp); 47 return; 48 } 49 do { 50 if (trace->skip > 0) 51 trace->skip--; 52 else 53 trace->entries[trace->nr_entries++] = pc; 54 if (trace->nr_entries >= trace->max_entries) 55 break; 56 pc = unwind_stack(tsk, &sp, pc, &ra); 57 } while (pc); 58 #else 59 save_raw_context_stack(trace, sp); 60 #endif 61 } 62 63 /* 64 * Save stack-backtrace addresses into a stack_trace buffer. 65 */ 66 void save_stack_trace(struct stack_trace *trace) 67 { 68 save_stack_trace_tsk(current, trace); 69 } 70 EXPORT_SYMBOL_GPL(save_stack_trace); 71 72 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 73 { 74 struct pt_regs dummyregs; 75 struct pt_regs *regs = &dummyregs; 76 77 WARN_ON(trace->nr_entries || !trace->max_entries); 78 79 if (tsk != current) { 80 regs->regs[29] = tsk->thread.reg29; 81 regs->regs[31] = 0; 82 regs->cp0_epc = tsk->thread.reg31; 83 } else 84 prepare_frametrace(regs); 85 save_context_stack(trace, tsk, regs); 86 } 87 EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 88