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 0; 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 .walk_stack = print_context_stack, 61 }; 62 63 static const struct stacktrace_ops save_stack_ops_nosched = { 64 .warning = save_stack_warning, 65 .warning_symbol = save_stack_warning_symbol, 66 .stack = save_stack_stack, 67 .address = save_stack_address_nosched, 68 .walk_stack = print_context_stack, 69 }; 70 71 /* 72 * Save stack-backtrace addresses into a stack_trace buffer. 73 */ 74 void save_stack_trace(struct stack_trace *trace) 75 { 76 dump_trace(current, NULL, NULL, 0, &save_stack_ops, trace); 77 if (trace->nr_entries < trace->max_entries) 78 trace->entries[trace->nr_entries++] = ULONG_MAX; 79 } 80 EXPORT_SYMBOL_GPL(save_stack_trace); 81 82 void save_stack_trace_bp(struct stack_trace *trace, unsigned long bp) 83 { 84 dump_trace(current, NULL, NULL, bp, &save_stack_ops, trace); 85 if (trace->nr_entries < trace->max_entries) 86 trace->entries[trace->nr_entries++] = ULONG_MAX; 87 } 88 89 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 90 { 91 dump_trace(tsk, NULL, NULL, 0, &save_stack_ops_nosched, trace); 92 if (trace->nr_entries < trace->max_entries) 93 trace->entries[trace->nr_entries++] = ULONG_MAX; 94 } 95 EXPORT_SYMBOL_GPL(save_stack_trace_tsk); 96 97 /* Userspace stacktrace - based on kernel/trace/trace_sysprof.c */ 98 99 struct stack_frame { 100 const void __user *next_fp; 101 unsigned long ret_addr; 102 }; 103 104 static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) 105 { 106 int ret; 107 108 if (!access_ok(VERIFY_READ, fp, sizeof(*frame))) 109 return 0; 110 111 ret = 1; 112 pagefault_disable(); 113 if (__copy_from_user_inatomic(frame, fp, sizeof(*frame))) 114 ret = 0; 115 pagefault_enable(); 116 117 return ret; 118 } 119 120 static inline void __save_stack_trace_user(struct stack_trace *trace) 121 { 122 const struct pt_regs *regs = task_pt_regs(current); 123 const void __user *fp = (const void __user *)regs->bp; 124 125 if (trace->nr_entries < trace->max_entries) 126 trace->entries[trace->nr_entries++] = regs->ip; 127 128 while (trace->nr_entries < trace->max_entries) { 129 struct stack_frame frame; 130 131 frame.next_fp = NULL; 132 frame.ret_addr = 0; 133 if (!copy_stack_frame(fp, &frame)) 134 break; 135 if ((unsigned long)fp < regs->sp) 136 break; 137 if (frame.ret_addr) { 138 trace->entries[trace->nr_entries++] = 139 frame.ret_addr; 140 } 141 if (fp == frame.next_fp) 142 break; 143 fp = frame.next_fp; 144 } 145 } 146 147 void save_stack_trace_user(struct stack_trace *trace) 148 { 149 /* 150 * Trace user stack if we are not a kernel thread 151 */ 152 if (current->mm) { 153 __save_stack_trace_user(trace); 154 } 155 if (trace->nr_entries < trace->max_entries) 156 trace->entries[trace->nr_entries++] = ULONG_MAX; 157 } 158 159