diff options
Diffstat (limited to 'kernel/trace/trace_functions_graph.c')
| -rw-r--r-- | kernel/trace/trace_functions_graph.c | 213 |
1 files changed, 168 insertions, 45 deletions
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 8b592418d8b..b3749a2c313 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
| @@ -52,12 +52,13 @@ static struct tracer_flags tracer_flags = { | |||
| 52 | .opts = trace_opts | 52 | .opts = trace_opts |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| 55 | /* pid on the last trace processed */ | 55 | static struct trace_array *graph_array; |
| 56 | 56 | ||
| 57 | 57 | ||
| 58 | /* Add a function return address to the trace stack on thread info.*/ | 58 | /* Add a function return address to the trace stack on thread info.*/ |
| 59 | int | 59 | int |
| 60 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth) | 60 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, |
| 61 | unsigned long frame_pointer) | ||
| 61 | { | 62 | { |
| 62 | unsigned long long calltime; | 63 | unsigned long long calltime; |
| 63 | int index; | 64 | int index; |
| @@ -85,6 +86,7 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth) | |||
| 85 | current->ret_stack[index].func = func; | 86 | current->ret_stack[index].func = func; |
| 86 | current->ret_stack[index].calltime = calltime; | 87 | current->ret_stack[index].calltime = calltime; |
| 87 | current->ret_stack[index].subtime = 0; | 88 | current->ret_stack[index].subtime = 0; |
| 89 | current->ret_stack[index].fp = frame_pointer; | ||
| 88 | *depth = index; | 90 | *depth = index; |
| 89 | 91 | ||
| 90 | return 0; | 92 | return 0; |
| @@ -92,7 +94,8 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth) | |||
| 92 | 94 | ||
| 93 | /* Retrieve a function return address to the trace stack on thread info.*/ | 95 | /* Retrieve a function return address to the trace stack on thread info.*/ |
| 94 | static void | 96 | static void |
| 95 | ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) | 97 | ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, |
| 98 | unsigned long frame_pointer) | ||
| 96 | { | 99 | { |
| 97 | int index; | 100 | int index; |
| 98 | 101 | ||
| @@ -106,6 +109,31 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) | |||
| 106 | return; | 109 | return; |
| 107 | } | 110 | } |
| 108 | 111 | ||
| 112 | #ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST | ||
| 113 | /* | ||
| 114 | * The arch may choose to record the frame pointer used | ||
| 115 | * and check it here to make sure that it is what we expect it | ||
| 116 | * to be. If gcc does not set the place holder of the return | ||
| 117 | * address in the frame pointer, and does a copy instead, then | ||
| 118 | * the function graph trace will fail. This test detects this | ||
| 119 | * case. | ||
| 120 | * | ||
| 121 | * Currently, x86_32 with optimize for size (-Os) makes the latest | ||
| 122 | * gcc do the above. | ||
| 123 | */ | ||
| 124 | if (unlikely(current->ret_stack[index].fp != frame_pointer)) { | ||
| 125 | ftrace_graph_stop(); | ||
| 126 | WARN(1, "Bad frame pointer: expected %lx, received %lx\n" | ||
| 127 | " from func %pF return to %lx\n", | ||
| 128 | current->ret_stack[index].fp, | ||
| 129 | frame_pointer, | ||
| 130 | (void *)current->ret_stack[index].func, | ||
| 131 | current->ret_stack[index].ret); | ||
| 132 | *ret = (unsigned long)panic; | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | #endif | ||
| 136 | |||
| 109 | *ret = current->ret_stack[index].ret; | 137 | *ret = current->ret_stack[index].ret; |
| 110 | trace->func = current->ret_stack[index].func; | 138 | trace->func = current->ret_stack[index].func; |
| 111 | trace->calltime = current->ret_stack[index].calltime; | 139 | trace->calltime = current->ret_stack[index].calltime; |
| @@ -117,12 +145,12 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) | |||
| 117 | * Send the trace to the ring-buffer. | 145 | * Send the trace to the ring-buffer. |
| 118 | * @return the original return address. | 146 | * @return the original return address. |
| 119 | */ | 147 | */ |
| 120 | unsigned long ftrace_return_to_handler(void) | 148 | unsigned long ftrace_return_to_handler(unsigned long frame_pointer) |
| 121 | { | 149 | { |
| 122 | struct ftrace_graph_ret trace; | 150 | struct ftrace_graph_ret trace; |
| 123 | unsigned long ret; | 151 | unsigned long ret; |
| 124 | 152 | ||
| 125 | ftrace_pop_return_trace(&trace, &ret); | 153 | ftrace_pop_return_trace(&trace, &ret, frame_pointer); |
| 126 | trace.rettime = trace_clock_local(); | 154 | trace.rettime = trace_clock_local(); |
| 127 | ftrace_graph_return(&trace); | 155 | ftrace_graph_return(&trace); |
| 128 | barrier(); | 156 | barrier(); |
| @@ -138,10 +166,123 @@ unsigned long ftrace_return_to_handler(void) | |||
| 138 | return ret; | 166 | return ret; |
| 139 | } | 167 | } |
| 140 | 168 | ||
| 169 | static int __trace_graph_entry(struct trace_array *tr, | ||
| 170 | struct ftrace_graph_ent *trace, | ||
| 171 | unsigned long flags, | ||
| 172 | int pc) | ||
| 173 | { | ||
| 174 | struct ftrace_event_call *call = &event_funcgraph_entry; | ||
| 175 | struct ring_buffer_event *event; | ||
| 176 | struct ring_buffer *buffer = tr->buffer; | ||
| 177 | struct ftrace_graph_ent_entry *entry; | ||
| 178 | |||
| 179 | if (unlikely(local_read(&__get_cpu_var(ftrace_cpu_disabled)))) | ||
| 180 | return 0; | ||
| 181 | |||
| 182 | event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_ENT, | ||
| 183 | sizeof(*entry), flags, pc); | ||
| 184 | if (!event) | ||
| 185 | return 0; | ||
| 186 | entry = ring_buffer_event_data(event); | ||
| 187 | entry->graph_ent = *trace; | ||
| 188 | if (!filter_current_check_discard(buffer, call, entry, event)) | ||
| 189 | ring_buffer_unlock_commit(buffer, event); | ||
| 190 | |||
| 191 | return 1; | ||
| 192 | } | ||
| 193 | |||
| 194 | int trace_graph_entry(struct ftrace_graph_ent *trace) | ||
| 195 | { | ||
| 196 | struct trace_array *tr = graph_array; | ||
| 197 | struct trace_array_cpu *data; | ||
| 198 | unsigned long flags; | ||
| 199 | long disabled; | ||
| 200 | int ret; | ||
| 201 | int cpu; | ||
| 202 | int pc; | ||
| 203 | |||
| 204 | if (unlikely(!tr)) | ||
| 205 | return 0; | ||
| 206 | |||
| 207 | if (!ftrace_trace_task(current)) | ||
| 208 | return 0; | ||
| 209 | |||
| 210 | if (!ftrace_graph_addr(trace->func)) | ||
| 211 | return 0; | ||
| 212 | |||
| 213 | local_irq_save(flags); | ||
| 214 | cpu = raw_smp_processor_id(); | ||
| 215 | data = tr->data[cpu]; | ||
| 216 | disabled = atomic_inc_return(&data->disabled); | ||
| 217 | if (likely(disabled == 1)) { | ||
| 218 | pc = preempt_count(); | ||
| 219 | ret = __trace_graph_entry(tr, trace, flags, pc); | ||
| 220 | } else { | ||
| 221 | ret = 0; | ||
| 222 | } | ||
| 223 | /* Only do the atomic if it is not already set */ | ||
| 224 | if (!test_tsk_trace_graph(current)) | ||
| 225 | set_tsk_trace_graph(current); | ||
| 226 | |||
| 227 | atomic_dec(&data->disabled); | ||
| 228 | local_irq_restore(flags); | ||
| 229 | |||
| 230 | return ret; | ||
| 231 | } | ||
| 232 | |||
| 233 | static void __trace_graph_return(struct trace_array *tr, | ||
| 234 | struct ftrace_graph_ret *trace, | ||
| 235 | unsigned long flags, | ||
| 236 | int pc) | ||
| 237 | { | ||
| 238 | struct ftrace_event_call *call = &event_funcgraph_exit; | ||
| 239 | struct ring_buffer_event *event; | ||
| 240 | struct ring_buffer *buffer = tr->buffer; | ||
| 241 | struct ftrace_graph_ret_entry *entry; | ||
| 242 | |||
| 243 | if (unlikely(local_read(&__get_cpu_var(ftrace_cpu_disabled)))) | ||
| 244 | return; | ||
| 245 | |||
| 246 | event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_RET, | ||
| 247 | sizeof(*entry), flags, pc); | ||
| 248 | if (!event) | ||
| 249 | return; | ||
| 250 | entry = ring_buffer_event_data(event); | ||
| 251 | entry->ret = *trace; | ||
| 252 | if (!filter_current_check_discard(buffer, call, entry, event)) | ||
| 253 | ring_buffer_unlock_commit(buffer, event); | ||
| 254 | } | ||
| 255 | |||
| 256 | void trace_graph_return(struct ftrace_graph_ret *trace) | ||
| 257 | { | ||
| 258 | struct trace_array *tr = graph_array; | ||
| 259 | struct trace_array_cpu *data; | ||
| 260 | unsigned long flags; | ||
| 261 | long disabled; | ||
| 262 | int cpu; | ||
| 263 | int pc; | ||
| 264 | |||
| 265 | local_irq_save(flags); | ||
| 266 | cpu = raw_smp_processor_id(); | ||
| 267 | data = tr->data[cpu]; | ||
| 268 | disabled = atomic_inc_return(&data->disabled); | ||
| 269 | if (likely(disabled == 1)) { | ||
| 270 | pc = preempt_count(); | ||
| 271 | __trace_graph_return(tr, trace, flags, pc); | ||
