1 /* 2 * Stack trace management functions 3 * 4 * Copyright (C) 2006-2009 Red Hat, Inc., Ingo Molnar <mingo@redhat.com> 5 */ 6 #include <linux/sched.h> 7 #include <linux/stacktrace.h> 8 #include <linux/module.h> 9 #include <linux/uaccess.h> 10 #include <asm/stacktrace.h> 11 12 static void save_stack_warning(void *data, char *msg) 13 { 14 } 15 16 static void 17 save_stack_warning_symbol(void *data, char *msg, unsigned long symbol) 18 { 19 } 20 21 static int save_stack_stack(void *data, char *name) 22 { 23 return -1; 24 } 25 26 static void save_stack_address(void *data, unsigned long addr, int reliable) 27 { 28 struct stack_trace *trace = data; 29 if (!reliable) 30 return; 31 if (trace->skip > 0) { 32 trace->skip--; 33 return; 34 } 35 if (trace->nr_entries < trace->max_entries) 36 trace->entries[trace->nr_entries++] = addr; 37 } 38 39 static void 40 save_stack_address_nosched(void *data, unsigned long addr, int reliable) 41 { 42 struct stack_trace *trace = (struct stack_trace *)data; 43 if (!reliable) 44 return; 45 if (in_sched_functions(addr)) 46 return; 47 if (trace->skip > 0) { 48 trace->skip--; 49 return; 50 } 51 if (trace->nr_entries < trace->max_entries) 52 trace->entries[trace->nr_entries++] = addr; 53 } 54 55 static const struct stacktrace_ops save_stack_ops = { 56 .warning = save_stack_warning, 57 .warning_symbol = save_stack_warning_symbol, 58 .stack = save_stack_stack, 59 .address = save_stack_address, 60 }; 61 62 static const struct stacktrace_ops save_stack_ops_nosched = { 63 .warning = save_stack_warning, 64 .warning_symbol = save_stack_warning_symbol, 65 .stack = save_stack_stack, 66 .address = save_stack_address_nosched, 67 }; 68 69 /* 70 * Save stack-backtrace addresses into a stack_trace buffer. 71 */ 72 void save_stack_trace(struct stack_trace *trace) 73 { 74 dump_trace(current, NULL, NULL, 0, &save_stack_ops, trace); 75 if (trace->nr_entries < trace->max_entries) 76 trace->entries[trace->nr_entries++] = ULONG_MAX; 77 } 78 EXPORT_SYMBOL_GPL(save_stack_trace); 79 80 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 81 { 82 dump_trace(tsk, NULL, NULL, 0, &save_stack_ops_nosched, trace); 83 if (trace->nr_entries < trace->max_entries) 84 trace->entries[trace->nr_entries++] = ULONG_MAX; 85 } 86 EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 87 88 /* Userspace stacktrace - based on kernel/trace/trace_sysprof.c */ 89 90 struct stack_frame { 91 const void __user *next_fp; 92 unsigned long ret_addr; 93 }; 94 95 static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) 96 { 97 int ret; 98 99 if (!access_ok(VERIFY_READ, fp, sizeof(*frame))) 100 return 0; 101 102 ret = 1; 103 pagefault_disable(); 104 if (__copy_from_user_inatomic(frame, fp, sizeof(*frame))) 105 ret = 0; 106 pagefault_enable(); 107 108 return ret; 109 } 110 111 static inline void __save_stack_trace_user(struct stack_trace *trace) 112 { 113 const struct pt_regs *regs = task_pt_regs(current); 114 const void __user *fp = (const void __user *)regs->bp; 115 116 if (trace->nr_entries < trace->max_entries) 117 trace->entries[trace->nr_entries++] = regs->ip; 118 119 while (trace->nr_entries < trace->max_entries) { 120 struct stack_frame frame; 121 122 frame.next_fp = NULL; 123 frame.ret_addr = 0; 124 if (!copy_stack_frame(fp, &frame)) 125 break; 126 if ((unsigned long)fp < regs->sp) 127 break; 128 if (frame.ret_addr) { 129 trace->entries[trace->nr_entries++] = 130 frame.ret_addr; 131 } 132 if (fp == frame.next_fp) 133 break; 134 fp = frame.next_fp; 135 } 136 } 137 138 void save_stack_trace_user(struct stack_trace *trace) 139 { 140 /* 141 * Trace user stack if we are not a kernel thread 142 */ 143 if (current->mm) { 144 __save_stack_trace_user(trace); 145 } 146 if (trace->nr_entries < trace->max_entries) 147 trace->entries[trace->nr_entries++] = ULONG_MAX; 148 } 149 150