1 /* 2 * Code for tracing calls in Linux kernel. 3 * Copyright (C) 2009 Helge Deller <deller@gmx.de> 4 * 5 * based on code for x86 which is: 6 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com> 7 * 8 * future possible enhancements: 9 * - add CONFIG_DYNAMIC_FTRACE 10 * - add CONFIG_STACK_TRACER 11 */ 12 13 #include <linux/init.h> 14 #include <linux/ftrace.h> 15 16 #include <asm/sections.h> 17 #include <asm/ftrace.h> 18 19 20 21 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 22 23 /* Add a function return address to the trace stack on thread info.*/ 24 static int push_return_trace(unsigned long ret, unsigned long long time, 25 unsigned long func, int *depth) 26 { 27 int index; 28 29 if (!current->ret_stack) 30 return -EBUSY; 31 32 /* The return trace stack is full */ 33 if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) { 34 atomic_inc(¤t->trace_overrun); 35 return -EBUSY; 36 } 37 38 index = ++current->curr_ret_stack; 39 barrier(); 40 current->ret_stack[index].ret = ret; 41 current->ret_stack[index].func = func; 42 current->ret_stack[index].calltime = time; 43 *depth = index; 44 45 return 0; 46 } 47 48 /* Retrieve a function return address to the trace stack on thread info.*/ 49 static void pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) 50 { 51 int index; 52 53 index = current->curr_ret_stack; 54 55 if (unlikely(index < 0)) { 56 ftrace_graph_stop(); 57 WARN_ON(1); 58 /* Might as well panic, otherwise we have no where to go */ 59 *ret = (unsigned long) 60 dereference_function_descriptor(&panic); 61 return; 62 } 63 64 *ret = current->ret_stack[index].ret; 65 trace->func = current->ret_stack[index].func; 66 trace->calltime = current->ret_stack[index].calltime; 67 trace->overrun = atomic_read(¤t->trace_overrun); 68 trace->depth = index; 69 barrier(); 70 current->curr_ret_stack--; 71 72 } 73 74 /* 75 * Send the trace to the ring-buffer. 76 * @return the original return address. 77 */ 78 unsigned long ftrace_return_to_handler(unsigned long retval0, 79 unsigned long retval1) 80 { 81 struct ftrace_graph_ret trace; 82 unsigned long ret; 83 84 pop_return_trace(&trace, &ret); 85 trace.rettime = local_clock(); 86 ftrace_graph_return(&trace); 87 88 if (unlikely(!ret)) { 89 ftrace_graph_stop(); 90 WARN_ON(1); 91 /* Might as well panic. What else to do? */ 92 ret = (unsigned long) 93 dereference_function_descriptor(&panic); 94 } 95 96 /* HACK: we hand over the old functions' return values 97 in %r23 and %r24. Assembly in entry.S will take care 98 and move those to their final registers %ret0 and %ret1 */ 99 asm( "copy %0, %%r23 \n\t" 100 "copy %1, %%r24 \n" : : "r" (retval0), "r" (retval1) ); 101 102 return ret; 103 } 104 105 /* 106 * Hook the return address and push it in the stack of return addrs 107 * in current thread info. 108 */ 109 void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) 110 { 111 unsigned long old; 112 unsigned long long calltime; 113 struct ftrace_graph_ent trace; 114 115 if (unlikely(ftrace_graph_is_dead())) 116 return; 117 118 if (unlikely(atomic_read(¤t->tracing_graph_pause))) 119 return; 120 121 old = *parent; 122 *parent = (unsigned long) 123 dereference_function_descriptor(&return_to_handler); 124 125 if (unlikely(!__kernel_text_address(old))) { 126 ftrace_graph_stop(); 127 *parent = old; 128 WARN_ON(1); 129 return; 130 } 131 132 calltime = local_clock(); 133 134 if (push_return_trace(old, calltime, 135 self_addr, &trace.depth) == -EBUSY) { 136 *parent = old; 137 return; 138 } 139 140 trace.func = self_addr; 141 142 /* Only trace if the calling function expects to */ 143 if (!ftrace_graph_entry(&trace)) { 144 current->curr_ret_stack--; 145 *parent = old; 146 } 147 } 148 149 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ 150 151 152 void ftrace_function_trampoline(unsigned long parent, 153 unsigned long self_addr, 154 unsigned long org_sp_gr3) 155 { 156 extern ftrace_func_t ftrace_trace_function; 157 158 if (ftrace_trace_function != ftrace_stub) { 159 ftrace_trace_function(parent, self_addr); 160 return; 161 } 162 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 163 if (ftrace_graph_entry && ftrace_graph_return) { 164 unsigned long sp; 165 unsigned long *parent_rp; 166 167 asm volatile ("copy %%r30, %0" : "=r"(sp)); 168 /* sanity check: is stack pointer which we got from 169 assembler function in entry.S in a reasonable 170 range compared to current stack pointer? */ 171 if ((sp - org_sp_gr3) > 0x400) 172 return; 173 174 /* calculate pointer to %rp in stack */ 175 parent_rp = (unsigned long *) org_sp_gr3 - 0x10; 176 /* sanity check: parent_rp should hold parent */ 177 if (*parent_rp != parent) 178 return; 179 180 prepare_ftrace_return(parent_rp, self_addr); 181 return; 182 } 183 #endif 184 } 185 186