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 8b592418d8b2..b3749a2c3132 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); | ||
272 | } | ||
273 | if (!trace->depth) | ||
274 | clear_tsk_trace_graph(current); | ||
275 | atomic_dec(&data->disabled); | ||
276 | local_irq_restore(flags); | ||
277 | } | ||
278 | |||
141 | static int graph_trace_init(struct trace_array *tr) | 279 | static int graph_trace_init(struct trace_array *tr) |
142 | { | 280 | { |
143 | int ret = register_ftrace_graph(&trace_graph_return, | 281 | int ret; |
144 | &trace_graph_entry); | 282 | |
283 | graph_array = tr; | ||
284 | ret = register_ftrace_graph(&trace_graph_return, | ||
285 | &trace_graph_entry); | ||
145 | if (ret) | 286 | if (ret) |
146 | return ret; | 287 | return ret; |
147 | tracing_start_cmdline_record(); | 288 | tracing_start_cmdline_record(); |
@@ -149,49 +290,30 @@ static int graph_trace_init(struct trace_array *tr) | |||
149 | return 0; | 290 | return 0; |
150 | } | 291 | } |
151 | 292 | ||
293 | void set_graph_array(struct trace_array *tr) | ||
294 | { | ||
295 | graph_array = tr; | ||
296 | } | ||
297 | |||
152 | static void graph_trace_reset(struct trace_array *tr) | 298 | static void graph_trace_reset(struct trace_array *tr) |
153 | { | 299 | { |
154 | tracing_stop_cmdline_record(); | 300 | tracing_stop_cmdline_record(); |
155 | unregister_ftrace_graph(); | 301 | unregister_ftrace_graph(); |
156 | } | 302 | } |
157 | 303 | ||
158 | static inline int log10_cpu(int nb) | 304 | static int max_bytes_for_cpu; |
159 | { | ||
160 | if (nb / 100) | ||
161 | return 3; | ||
162 | if (nb / 10) | ||
163 | return 2; | ||
164 | return 1; | ||
165 | } | ||
166 | 305 | ||
167 | static enum print_line_t | 306 | static enum print_line_t |
168 | print_graph_cpu(struct trace_seq *s, int cpu) | 307 | print_graph_cpu(struct trace_seq *s, int cpu) |
169 | { | 308 | { |
170 | int i; | ||
171 | int ret; | 309 | int ret; |
172 | int log10_this = log10_cpu(cpu); | ||
173 | int log10_all = log10_cpu(cpumask_weight(cpu_online_mask)); | ||
174 | |||
175 | 310 | ||
176 | /* | 311 | /* |
177 | * Start with a space character - to make it stand out | 312 | * Start with a space character - to make it stand out |
178 | * to the right a bit when trace output is pasted into | 313 | * to the right a bit when trace output is pasted into |
179 | * email: | 314 | * email: |
180 | */ | 315 | */ |
181 | ret = trace_seq_printf(s, " "); | 316 | ret = trace_seq_printf(s, " %*d) ", max_bytes_for_cpu, cpu); |
182 | |||
183 | /* | ||
184 | * Tricky - we space the CPU field according to the max | ||
185 | * number of online CPUs. On a 2-cpu system it would take | ||
186 | * a maximum of 1 digit - on a 128 cpu system it would | ||
187 | * take up to 3 digits: | ||
188 | */ | ||
189 | for (i = 0; i < log10_all - log10_this; i++) { | ||
190 | ret = trace_seq_printf(s, " "); | ||
191 | if (!ret) | ||
192 | return TRACE_TYPE_PARTIAL_LINE; | ||
193 | } | ||
194 | ret = trace_seq_printf(s, "%d) ", cpu); | ||
195 | if (!ret) | 317 | if (!ret) |
196 | return TRACE_TYPE_PARTIAL_LINE; | 318 | return TRACE_TYPE_PARTIAL_LINE; |
197 | 319 | ||
@@ -537,11 +659,7 @@ print_graph_entry_leaf(struct trace_iterator *iter, | |||
537 | return TRACE_TYPE_PARTIAL_LINE; | 659 | return TRACE_TYPE_PARTIAL_LINE; |
538 | } | 660 | } |
539 | 661 | ||
540 | ret = seq_print_ip_sym(s, call->func, 0); | 662 | ret = trace_seq_printf(s, "%pf();\n", (void *)call->func); |
541 | if (!ret) | ||
542 | return TRACE_TYPE_PARTIAL_LINE; | ||
543 | |||
544 | ret = trace_seq_printf(s, "();\n"); | ||
545 | if (!ret) | 663 | if (!ret) |
546 | return TRACE_TYPE_PARTIAL_LINE; | 664 | return TRACE_TYPE_PARTIAL_LINE; |
547 | 665 | ||
@@ -584,11 +702,7 @@ print_graph_entry_nested(struct trace_iterator *iter, | |||
584 | return TRACE_TYPE_PARTIAL_LINE; | 702 | return TRACE_TYPE_PARTIAL_LINE; |
585 | } | 703 | } |
586 | 704 | ||
587 | ret = seq_print_ip_sym(s, call->func, 0); | 705 | ret = trace_seq_printf(s, "%pf() {\n", (void *)call->func); |
588 | if (!ret) | ||
589 | return TRACE_TYPE_PARTIAL_LINE; | ||
590 | |||
591 | ret = trace_seq_printf(s, "() {\n"); | ||
592 | if (!ret) | 706 | if (!ret) |
593 | return TRACE_TYPE_PARTIAL_LINE; | 707 | return TRACE_TYPE_PARTIAL_LINE; |
594 | 708 | ||
@@ -815,9 +929,16 @@ print_graph_function(struct trace_iterator *iter) | |||
815 | 929 | ||
816 | switch (entry->type) { | 930 | switch (entry->type) { |
817 | case TRACE_GRAPH_ENT: { | 931 | case TRACE_GRAPH_ENT: { |
818 | struct ftrace_graph_ent_entry *field; | 932 | /* |
933 | * print_graph_entry() may consume the current event, | ||
934 | * thus @field may become invalid, so we need to save it. | ||
935 | * sizeof(struct ftrace_graph_ent_entry) is very small, | ||
936 | * it can be safely saved at the stack. | ||
937 | */ | ||
938 | struct ftrace_graph_ent_entry *field, saved; | ||
819 | trace_assign_type(field, entry); | 939 | trace_assign_type(field, entry); |
820 | return print_graph_entry(field, s, iter); | 940 | saved = *field; |
941 | return print_graph_entry(&saved, s, iter); | ||
821 | } | 942 | } |
822 | case TRACE_GRAPH_RET: { | 943 | case TRACE_GRAPH_RET: { |
823 | struct ftrace_graph_ret_entry *field; | 944 | struct ftrace_graph_ret_entry *field; |
@@ -899,6 +1020,8 @@ static struct tracer graph_trace __read_mostly = { | |||
899 | 1020 | ||
900 | static __init int init_graph_trace(void) | 1021 | static __init int init_graph_trace(void) |
901 | { | 1022 | { |
1023 | max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1); | ||
1024 | |||
902 | return register_tracer(&graph_trace); | 1025 | return register_tracer(&graph_trace); |
903 | } | 1026 | } |
904 | 1027 | ||