1 /* 2 * arch/mips/kernel/stacktrace.c 3 * 4 * Stack trace management functions 5 * 6 * Copyright (C) 2006 Atsushi Nemoto <[email protected]> 7 */ 8 #include <linux/sched.h> 9 #include <linux/stacktrace.h> 10 #include <asm/stacktrace.h> 11 12 /* 13 * Save stack-backtrace addresses into a stack_trace buffer: 14 */ 15 static void save_raw_context_stack(struct stack_trace *trace, 16 unsigned int skip, unsigned long reg29) 17 { 18 unsigned long *sp = (unsigned long *)reg29; 19 unsigned long addr; 20 21 while (!kstack_end(sp)) { 22 addr = *sp++; 23 if (__kernel_text_address(addr)) { 24 if (!skip) 25 trace->entries[trace->nr_entries++] = addr; 26 else 27 skip--; 28 if (trace->nr_entries >= trace->max_entries) 29 break; 30 } 31 } 32 } 33 34 static struct pt_regs * save_context_stack(struct stack_trace *trace, 35 unsigned int skip, struct task_struct *task, struct pt_regs *regs) 36 { 37 unsigned long sp = regs->regs[29]; 38 #ifdef CONFIG_KALLSYMS 39 unsigned long ra = regs->regs[31]; 40 unsigned long pc = regs->cp0_epc; 41 extern void ret_from_irq(void); 42 43 if (raw_show_trace || !__kernel_text_address(pc)) { 44 save_raw_context_stack(trace, skip, sp); 45 return NULL; 46 } 47 do { 48 if (!skip) 49 trace->entries[trace->nr_entries++] = pc; 50 else 51 skip--; 52 if (trace->nr_entries >= trace->max_entries) 53 break; 54 /* 55 * If we reached the bottom of interrupt context, 56 * return saved pt_regs. 57 */ 58 if (pc == (unsigned long)ret_from_irq) { 59 unsigned long stack_page = 60 (unsigned long)task_stack_page(task); 61 if (!stack_page || 62 sp < stack_page || 63 sp > stack_page + THREAD_SIZE - 32) 64 break; 65 return (struct pt_regs *)sp; 66 } 67 pc = unwind_stack(task, &sp, pc, ra); 68 ra = 0; 69 } while (pc); 70 #else 71 save_raw_context_stack(sp); 72 #endif 73 74 return NULL; 75 } 76 77 /* 78 * Save stack-backtrace addresses into a stack_trace buffer. 79 * If all_contexts is set, all contexts (hardirq, softirq and process) 80 * are saved. If not set then only the current context is saved. 81 */ 82 void save_stack_trace(struct stack_trace *trace, 83 struct task_struct *task, int all_contexts, 84 unsigned int skip) 85 { 86 struct pt_regs dummyregs; 87 struct pt_regs *regs = &dummyregs; 88 89 WARN_ON(trace->nr_entries || !trace->max_entries); 90 91 if (task && task != current) { 92 regs->regs[29] = task->thread.reg29; 93 regs->regs[31] = 0; 94 regs->cp0_epc = task->thread.reg31; 95 } else { 96 if (!task) 97 task = current; 98 prepare_frametrace(regs); 99 } 100 101 while (1) { 102 regs = save_context_stack(trace, skip, task, regs); 103 if (!all_contexts || !regs || 104 trace->nr_entries >= trace->max_entries) 105 break; 106 trace->entries[trace->nr_entries++] = ULONG_MAX; 107 if (trace->nr_entries >= trace->max_entries) 108 break; 109 skip = 0; 110 } 111 } 112