diff options
Diffstat (limited to 'kernel/trace/trace_functions_graph.c')
-rw-r--r-- | kernel/trace/trace_functions_graph.c | 693 |
1 files changed, 482 insertions, 211 deletions
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 930c08e5b38e..420ec3487579 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * | 2 | * |
3 | * Function graph tracer. | 3 | * Function graph tracer. |
4 | * Copyright (c) 2008 Frederic Weisbecker <fweisbec@gmail.com> | 4 | * Copyright (c) 2008-2009 Frederic Weisbecker <fweisbec@gmail.com> |
5 | * Mostly borrowed from function tracer which | 5 | * Mostly borrowed from function tracer which |
6 | * is Copyright (c) Steven Rostedt <srostedt@redhat.com> | 6 | * is Copyright (c) Steven Rostedt <srostedt@redhat.com> |
7 | * | 7 | * |
@@ -12,6 +12,12 @@ | |||
12 | #include <linux/fs.h> | 12 | #include <linux/fs.h> |
13 | 13 | ||
14 | #include "trace.h" | 14 | #include "trace.h" |
15 | #include "trace_output.h" | ||
16 | |||
17 | struct fgraph_data { | ||
18 | pid_t last_pid; | ||
19 | int depth; | ||
20 | }; | ||
15 | 21 | ||
16 | #define TRACE_GRAPH_INDENT 2 | 22 | #define TRACE_GRAPH_INDENT 2 |
17 | 23 | ||
@@ -20,9 +26,11 @@ | |||
20 | #define TRACE_GRAPH_PRINT_CPU 0x2 | 26 | #define TRACE_GRAPH_PRINT_CPU 0x2 |
21 | #define TRACE_GRAPH_PRINT_OVERHEAD 0x4 | 27 | #define TRACE_GRAPH_PRINT_OVERHEAD 0x4 |
22 | #define TRACE_GRAPH_PRINT_PROC 0x8 | 28 | #define TRACE_GRAPH_PRINT_PROC 0x8 |
29 | #define TRACE_GRAPH_PRINT_DURATION 0x10 | ||
30 | #define TRACE_GRAPH_PRINT_ABS_TIME 0X20 | ||
23 | 31 | ||
24 | static struct tracer_opt trace_opts[] = { | 32 | static struct tracer_opt trace_opts[] = { |
25 | /* Display overruns ? */ | 33 | /* Display overruns? (for self-debug purpose) */ |
26 | { TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) }, | 34 | { TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) }, |
27 | /* Display CPU ? */ | 35 | /* Display CPU ? */ |
28 | { TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) }, | 36 | { TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) }, |
@@ -30,26 +38,137 @@ static struct tracer_opt trace_opts[] = { | |||
30 | { TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) }, | 38 | { TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) }, |
31 | /* Display proc name/pid */ | 39 | /* Display proc name/pid */ |
32 | { TRACER_OPT(funcgraph-proc, TRACE_GRAPH_PRINT_PROC) }, | 40 | { TRACER_OPT(funcgraph-proc, TRACE_GRAPH_PRINT_PROC) }, |
41 | /* Display duration of execution */ | ||
42 | { TRACER_OPT(funcgraph-duration, TRACE_GRAPH_PRINT_DURATION) }, | ||
43 | /* Display absolute time of an entry */ | ||
44 | { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) }, | ||
33 | { } /* Empty entry */ | 45 | { } /* Empty entry */ |
34 | }; | 46 | }; |
35 | 47 | ||
36 | static struct tracer_flags tracer_flags = { | 48 | static struct tracer_flags tracer_flags = { |
37 | /* Don't display overruns and proc by default */ | 49 | /* Don't display overruns and proc by default */ |
38 | .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD, | 50 | .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD | |
51 | TRACE_GRAPH_PRINT_DURATION, | ||
39 | .opts = trace_opts | 52 | .opts = trace_opts |
40 | }; | 53 | }; |
41 | 54 | ||
42 | /* pid on the last trace processed */ | 55 | /* pid on the last trace processed */ |
43 | static pid_t last_pid[NR_CPUS] = { [0 ... NR_CPUS-1] = -1 }; | ||
44 | 56 | ||
45 | static int graph_trace_init(struct trace_array *tr) | 57 | |
58 | /* Add a function return address to the trace stack on thread info.*/ | ||
59 | int | ||
60 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, | ||
61 | unsigned long frame_pointer) | ||
62 | { | ||
63 | unsigned long long calltime; | ||
64 | int index; | ||
65 | |||
66 | if (!current->ret_stack) | ||
67 | return -EBUSY; | ||
68 | |||
69 | /* | ||
70 | * We must make sure the ret_stack is tested before we read | ||
71 | * anything else. | ||
72 | */ | ||
73 | smp_rmb(); | ||
74 | |||
75 | /* The return trace stack is full */ | ||
76 | if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) { | ||
77 | atomic_inc(¤t->trace_overrun); | ||
78 | return -EBUSY; | ||
79 | } | ||
80 | |||
81 | calltime = trace_clock_local(); | ||
82 | |||
83 | index = ++current->curr_ret_stack; | ||
84 | barrier(); | ||
85 | current->ret_stack[index].ret = ret; | ||
86 | current->ret_stack[index].func = func; | ||
87 | current->ret_stack[index].calltime = calltime; | ||
88 | current->ret_stack[index].subtime = 0; | ||
89 | current->ret_stack[index].fp = frame_pointer; | ||
90 | *depth = index; | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | /* Retrieve a function return address to the trace stack on thread info.*/ | ||
96 | static void | ||
97 | ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, | ||
98 | unsigned long frame_pointer) | ||
46 | { | 99 | { |
47 | int cpu, ret; | 100 | int index; |
48 | 101 | ||
49 | for_each_online_cpu(cpu) | 102 | index = current->curr_ret_stack; |
50 | tracing_reset(tr, cpu); | ||
51 | 103 | ||
52 | ret = register_ftrace_graph(&trace_graph_return, | 104 | if (unlikely(index < 0)) { |
105 | ftrace_graph_stop(); | ||
106 | WARN_ON(1); | ||
107 | /* Might as well panic, otherwise we have no where to go */ | ||
108 | *ret = (unsigned long)panic; | ||
109 | return; | ||
110 | } | ||
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 | |||
137 | *ret = current->ret_stack[index].ret; | ||
138 | trace->func = current->ret_stack[index].func; | ||
139 | trace->calltime = current->ret_stack[index].calltime; | ||
140 | trace->overrun = atomic_read(¤t->trace_overrun); | ||
141 | trace->depth = index; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * Send the trace to the ring-buffer. | ||
146 | * @return the original return address. | ||
147 | */ | ||
148 | unsigned long ftrace_return_to_handler(unsigned long frame_pointer) | ||
149 | { | ||
150 | struct ftrace_graph_ret trace; | ||
151 | unsigned long ret; | ||
152 | |||
153 | ftrace_pop_return_trace(&trace, &ret, frame_pointer); | ||
154 | trace.rettime = trace_clock_local(); | ||
155 | ftrace_graph_return(&trace); | ||
156 | barrier(); | ||
157 | current->curr_ret_stack--; | ||
158 | |||
159 | if (unlikely(!ret)) { | ||
160 | ftrace_graph_stop(); | ||
161 | WARN_ON(1); | ||
162 | /* Might as well panic. What else to do? */ | ||
163 | ret = (unsigned long)panic; | ||
164 | } | ||
165 | |||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | static int graph_trace_init(struct trace_array *tr) | ||
170 | { | ||
171 | int ret = register_ftrace_graph(&trace_graph_return, | ||
53 | &trace_graph_entry); | 172 | &trace_graph_entry); |
54 | if (ret) | 173 | if (ret) |
55 | return ret; | 174 | return ret; |
@@ -112,15 +231,15 @@ print_graph_cpu(struct trace_seq *s, int cpu) | |||
112 | static enum print_line_t | 231 | static enum print_line_t |
113 | print_graph_proc(struct trace_seq *s, pid_t pid) | 232 | print_graph_proc(struct trace_seq *s, pid_t pid) |
114 | { | 233 | { |
115 | int i; | 234 | char comm[TASK_COMM_LEN]; |
116 | int ret; | ||
117 | int len; | ||
118 | char comm[8]; | ||
119 | int spaces = 0; | ||
120 | /* sign + log10(MAX_INT) + '\0' */ | 235 | /* sign + log10(MAX_INT) + '\0' */ |
121 | char pid_str[11]; | 236 | char pid_str[11]; |
237 | int spaces = 0; | ||
238 | int ret; | ||
239 | int len; | ||
240 | int i; | ||
122 | 241 | ||
123 | strncpy(comm, trace_find_cmdline(pid), 7); | 242 | trace_find_cmdline(pid, comm); |
124 | comm[7] = '\0'; | 243 | comm[7] = '\0'; |
125 | sprintf(pid_str, "%d", pid); | 244 | sprintf(pid_str, "%d", pid); |
126 | 245 | ||
@@ -153,17 +272,25 @@ print_graph_proc(struct trace_seq *s, pid_t pid) | |||
153 | 272 | ||
154 | /* If the pid changed since the last trace, output this event */ | 273 | /* If the pid changed since the last trace, output this event */ |
155 | static enum print_line_t | 274 | static enum print_line_t |
156 | verif_pid(struct trace_seq *s, pid_t pid, int cpu) | 275 | verif_pid(struct trace_seq *s, pid_t pid, int cpu, struct fgraph_data *data) |
157 | { | 276 | { |
158 | pid_t prev_pid; | 277 | pid_t prev_pid; |
278 | pid_t *last_pid; | ||
159 | int ret; | 279 | int ret; |
160 | 280 | ||
161 | if (last_pid[cpu] != -1 && last_pid[cpu] == pid) | 281 | if (!data) |
282 | return TRACE_TYPE_HANDLED; | ||
283 | |||
284 | last_pid = &(per_cpu_ptr(data, cpu)->last_pid); | ||
285 | |||
286 | if (*last_pid == pid) | ||
162 | return TRACE_TYPE_HANDLED; | 287 | return TRACE_TYPE_HANDLED; |
163 | 288 | ||
164 | prev_pid = last_pid[cpu]; | 289 | prev_pid = *last_pid; |
165 | last_pid[cpu] = pid; | 290 | *last_pid = pid; |
166 | 291 | ||
292 | if (prev_pid == -1) | ||
293 | return TRACE_TYPE_HANDLED; | ||
167 | /* | 294 | /* |
168 | * Context-switch trace line: | 295 | * Context-switch trace line: |
169 | 296 | ||
@@ -175,34 +302,34 @@ verif_pid(struct trace_seq *s, pid_t pid, int cpu) | |||
175 | ret = trace_seq_printf(s, | 302 | ret = trace_seq_printf(s, |
176 | " ------------------------------------------\n"); | 303 | " ------------------------------------------\n"); |
177 | if (!ret) | 304 | if (!ret) |
178 | TRACE_TYPE_PARTIAL_LINE; | 305 | return TRACE_TYPE_PARTIAL_LINE; |
179 | 306 | ||
180 | ret = print_graph_cpu(s, cpu); | 307 | ret = print_graph_cpu(s, cpu); |
181 | if (ret == TRACE_TYPE_PARTIAL_LINE) | 308 | if (ret == TRACE_TYPE_PARTIAL_LINE) |
182 | TRACE_TYPE_PARTIAL_LINE; | 309 | return TRACE_TYPE_PARTIAL_LINE; |
183 | 310 | ||
184 | ret = print_graph_proc(s, prev_pid); | 311 | ret = print_graph_proc(s, prev_pid); |
185 | if (ret == TRACE_TYPE_PARTIAL_LINE) | 312 | if (ret == TRACE_TYPE_PARTIAL_LINE) |
186 | TRACE_TYPE_PARTIAL_LINE; | 313 | return TRACE_TYPE_PARTIAL_LINE; |
187 | 314 | ||
188 | ret = trace_seq_printf(s, " => "); | 315 | ret = trace_seq_printf(s, " => "); |
189 | if (!ret) | 316 | if (!ret) |
190 | TRACE_TYPE_PARTIAL_LINE; | 317 | return TRACE_TYPE_PARTIAL_LINE; |
191 | 318 | ||
192 | ret = print_graph_proc(s, pid); | 319 | ret = print_graph_proc(s, pid); |
193 | if (ret == TRACE_TYPE_PARTIAL_LINE) | 320 | if (ret == TRACE_TYPE_PARTIAL_LINE) |
194 | TRACE_TYPE_PARTIAL_LINE; | 321 | return TRACE_TYPE_PARTIAL_LINE; |
195 | 322 | ||
196 | ret = trace_seq_printf(s, | 323 | ret = trace_seq_printf(s, |
197 | "\n ------------------------------------------\n\n"); | 324 | "\n ------------------------------------------\n\n"); |
198 | if (!ret) | 325 | if (!ret) |
199 | TRACE_TYPE_PARTIAL_LINE; | 326 | return TRACE_TYPE_PARTIAL_LINE; |
200 | 327 | ||
201 | return ret; | 328 | return TRACE_TYPE_HANDLED; |
202 | } | 329 | } |
203 | 330 | ||
204 | static bool | 331 | static struct ftrace_graph_ret_entry * |
205 | trace_branch_is_leaf(struct trace_iterator *iter, | 332 | get_return_for_leaf(struct trace_iterator *iter, |
206 | struct ftrace_graph_ent_entry *curr) | 333 | struct ftrace_graph_ent_entry *curr) |
207 | { | 334 | { |
208 | struct ring_buffer_iter *ring_iter; | 335 | struct ring_buffer_iter *ring_iter; |
@@ -211,72 +338,130 @@ trace_branch_is_leaf(struct trace_iterator *iter, | |||
211 | 338 | ||
212 | ring_iter = iter->buffer_iter[iter->cpu]; | 339 | ring_iter = iter->buffer_iter[iter->cpu]; |
213 | 340 | ||
214 | if (!ring_iter) | 341 | /* First peek to compare current entry and the next one */ |
215 | return false; | 342 | if (ring_iter) |
216 | 343 | event = ring_buffer_iter_peek(ring_iter, NULL); | |
217 | event = ring_buffer_iter_peek(ring_iter, NULL); | 344 | else { |
345 | /* We need to consume the current entry to see the next one */ | ||
346 | ring_buffer_consume(iter->tr->buffer, iter->cpu, NULL); | ||
347 | event = ring_buffer_peek(iter->tr->buffer, iter->cpu, | ||
348 | NULL); | ||
349 | } | ||
218 | 350 | ||
219 | if (!event) | 351 | if (!event) |
220 | return false; | 352 | return NULL; |
221 | 353 | ||
222 | next = ring_buffer_event_data(event); | 354 | next = ring_buffer_event_data(event); |
223 | 355 | ||
224 | if (next->ent.type != TRACE_GRAPH_RET) | 356 | if (next->ent.type != TRACE_GRAPH_RET) |
225 | return false; | 357 | return NULL; |
226 | 358 | ||
227 | if (curr->ent.pid != next->ent.pid || | 359 | if (curr->ent.pid != next->ent.pid || |
228 | curr->graph_ent.func != next->ret.func) | 360 | curr->graph_ent.func != next->ret.func) |
229 | return false; | 361 | return NULL; |
362 | |||
363 | /* this is a leaf, now advance the iterator */ | ||
364 | if (ring_iter) | ||
365 | ring_buffer_read(ring_iter, NULL); | ||
366 | |||
367 | return next; | ||
368 | } | ||
369 | |||
370 | /* Signal a overhead of time execution to the output */ | ||
371 | static int | ||
372 | print_graph_overhead(unsigned long long duration, struct trace_seq *s) | ||
373 | { | ||
374 | /* If duration disappear, we don't need anything */ | ||
375 | if (!(tracer_flags.val & TRACE_GRAPH_PRINT_DURATION)) | ||
376 | return 1; | ||
377 | |||
378 | /* Non nested entry or return */ | ||
379 | if (duration == -1) | ||
380 | return trace_seq_printf(s, " "); | ||
381 | |||
382 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { | ||
383 | /* Duration exceeded 100 msecs */ | ||
384 | if (duration > 100000ULL) | ||
385 | return trace_seq_printf(s, "! "); | ||
386 | |||
387 | /* Duration exceeded 10 msecs */ | ||
388 | if (duration > 10000ULL) | ||
389 | return trace_seq_printf(s, "+ "); | ||
390 | } | ||
230 | 391 | ||
231 | return true; | 392 | return trace_seq_printf(s, " "); |
393 | } | ||
394 | |||
395 | static int print_graph_abs_time(u64 t, struct trace_seq *s) | ||
396 | { | ||
397 | unsigned long usecs_rem; | ||
398 | |||
399 | usecs_rem = do_div(t, NSEC_PER_SEC); | ||
400 | usecs_rem /= 1000; | ||
401 | |||
402 | return trace_seq_printf(s, "%5lu.%06lu | ", | ||
403 | (unsigned long)t, usecs_rem); | ||
232 | } | 404 | } |
233 | 405 | ||
234 | static enum print_line_t | 406 | static enum print_line_t |
235 | print_graph_irq(struct trace_seq *s, unsigned long addr, | 407 | print_graph_irq(struct trace_iterator *iter, unsigned long addr, |
236 | enum trace_type type, int cpu, pid_t pid) | 408 | enum trace_type type, int cpu, pid_t pid) |
237 | { | 409 | { |
238 | int ret; | 410 | int ret; |
411 | struct trace_seq *s = &iter->seq; | ||
239 | 412 | ||
240 | if (addr < (unsigned long)__irqentry_text_start || | 413 | if (addr < (unsigned long)__irqentry_text_start || |
241 | addr >= (unsigned long)__irqentry_text_end) | 414 | addr >= (unsigned long)__irqentry_text_end) |
242 | return TRACE_TYPE_UNHANDLED; | 415 | return TRACE_TYPE_UNHANDLED; |
243 | 416 | ||
244 | if (type == TRACE_GRAPH_ENT) { | 417 | /* Absolute time */ |
245 | ret = trace_seq_printf(s, "==========> | "); | 418 | if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) { |
246 | } else { | 419 | ret = print_graph_abs_time(iter->ts, s); |
247 | /* Cpu */ | 420 | if (!ret) |
248 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { | 421 | return TRACE_TYPE_PARTIAL_LINE; |
249 | ret = print_graph_cpu(s, cpu); | 422 | } |
250 | if (ret == TRACE_TYPE_PARTIAL_LINE) | ||
251 | return TRACE_TYPE_PARTIAL_LINE; | ||
252 | } | ||
253 | /* Proc */ | ||
254 | if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { | ||
255 | ret = print_graph_proc(s, pid); | ||
256 | if (ret == TRACE_TYPE_PARTIAL_LINE) | ||
257 | return TRACE_TYPE_PARTIAL_LINE; | ||
258 | 423 | ||
259 | ret = trace_seq_printf(s, " | "); | 424 | /* Cpu */ |
260 | if (!ret) | 425 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { |
261 | return TRACE_TYPE_PARTIAL_LINE; | 426 | ret = print_graph_cpu(s, cpu); |
262 | } | 427 | if (ret == TRACE_TYPE_PARTIAL_LINE) |
428 | return TRACE_TYPE_PARTIAL_LINE; | ||
429 | } | ||
430 | /* Proc */ | ||
431 | if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { | ||
432 | ret = print_graph_proc(s, pid); | ||
433 | if (ret == TRACE_TYPE_PARTIAL_LINE) | ||
434 | return TRACE_TYPE_PARTIAL_LINE; | ||
435 | ret = trace_seq_printf(s, " | "); | ||
436 | if (!ret) | ||
437 | return TRACE_TYPE_PARTIAL_LINE; | ||
438 | } | ||
263 | 439 | ||
264 | /* No overhead */ | 440 | /* No overhead */ |
265 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { | 441 | ret = print_graph_overhead(-1, s); |
266 | ret = trace_seq_printf(s, " "); | 442 | if (!ret) |
267 | if (!ret) | 443 | return TRACE_TYPE_PARTIAL_LINE; |
268 | return TRACE_TYPE_PARTIAL_LINE; | 444 | |
269 | } | 445 | if (type == TRACE_GRAPH_ENT) |
446 | ret = trace_seq_printf(s, "==========>"); | ||
447 | else | ||
448 | ret = trace_seq_printf(s, "<=========="); | ||
449 | |||
450 | if (!ret) | ||
451 | return TRACE_TYPE_PARTIAL_LINE; | ||
452 | |||
453 | /* Don't close the duration column if haven't one */ | ||
454 | if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) | ||
455 | trace_seq_printf(s, " |"); | ||
456 | ret = trace_seq_printf(s, "\n"); | ||
270 | 457 | ||
271 | ret = trace_seq_printf(s, "<========== |\n"); | ||
272 | } | ||
273 | if (!ret) | 458 | if (!ret) |
274 | return TRACE_TYPE_PARTIAL_LINE; | 459 | return TRACE_TYPE_PARTIAL_LINE; |
275 | return TRACE_TYPE_HANDLED; | 460 | return TRACE_TYPE_HANDLED; |
276 | } | 461 | } |
277 | 462 | ||
278 | static enum print_line_t | 463 | enum print_line_t |
279 | print_graph_duration(unsigned long long duration, struct trace_seq *s) | 464 | trace_print_graph_duration(unsigned long long duration, struct trace_seq *s) |
280 | { | 465 | { |
281 | unsigned long nsecs_rem = do_div(duration, 1000); | 466 | unsigned long nsecs_rem = do_div(duration, 1000); |
282 | /* log10(ULONG_MAX) + '\0' */ | 467 | /* log10(ULONG_MAX) + '\0' */ |
@@ -288,7 +473,7 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s) | |||
288 | sprintf(msecs_str, "%lu", (unsigned long) duration); | 473 | sprintf(msecs_str, "%lu", (unsigned long) duration); |
289 | 474 | ||
290 | /* Print msecs */ | 475 | /* Print msecs */ |
291 | ret = trace_seq_printf(s, msecs_str); | 476 | ret = trace_seq_printf(s, "%s", msecs_str); |
292 | if (!ret) | 477 | if (!ret) |
293 | return TRACE_TYPE_PARTIAL_LINE; | 478 | return TRACE_TYPE_PARTIAL_LINE; |
294 | 479 | ||
@@ -313,60 +498,66 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s) | |||
313 | if (!ret) | 498 | if (!ret) |
314 | return TRACE_TYPE_PARTIAL_LINE; | 499 | return TRACE_TYPE_PARTIAL_LINE; |
315 | } | 500 | } |
316 | |||
317 | ret = trace_seq_printf(s, "| "); | ||
318 | if (!ret) | ||
319 | return TRACE_TYPE_PARTIAL_LINE; | ||
320 | return TRACE_TYPE_HANDLED; | 501 | return TRACE_TYPE_HANDLED; |
321 | |||
322 | } | 502 | } |
323 | 503 | ||
324 | /* Signal a overhead of time execution to the output */ | 504 | static enum print_line_t |
325 | static int | 505 | print_graph_duration(unsigned long long duration, struct trace_seq *s) |
326 | print_graph_overhead(unsigned long long duration, struct trace_seq *s) | ||
327 | { | 506 | { |
328 | /* Duration exceeded 100 msecs */ | 507 | int ret; |
329 | if (duration > 100000ULL) | ||
330 | return trace_seq_printf(s, "! "); | ||
331 | 508 | ||
332 | /* Duration exceeded 10 msecs */ | 509 | ret = trace_print_graph_duration(duration, s); |
333 | if (duration > 10000ULL) | 510 | if (ret != TRACE_TYPE_HANDLED) |
334 | return trace_seq_printf(s, "+ "); | 511 | return ret; |
335 | 512 | ||
336 | return trace_seq_printf(s, " "); | 513 | ret = trace_seq_printf(s, "| "); |
514 | if (!ret) | ||
515 | return TRACE_TYPE_PARTIAL_LINE; | ||
516 | |||
517 | return TRACE_TYPE_HANDLED; | ||
337 | } | 518 | } |
338 | 519 | ||
339 | /* Case of a leaf function on its call entry */ | 520 | /* Case of a leaf function on its call entry */ |
340 | static enum print_line_t | 521 | static enum print_line_t |
341 | print_graph_entry_leaf(struct trace_iterator *iter, | 522 | print_graph_entry_leaf(struct trace_iterator *iter, |
342 | struct ftrace_graph_ent_entry *entry, struct trace_seq *s) | 523 | struct ftrace_graph_ent_entry *entry, |
524 | struct ftrace_graph_ret_entry *ret_entry, struct trace_seq *s) | ||
343 | { | 525 | { |
344 | struct ftrace_graph_ret_entry *ret_entry; | 526 | struct fgraph_data *data = iter->private; |
345 | struct ftrace_graph_ret *graph_ret; | 527 | struct ftrace_graph_ret *graph_ret; |
346 | struct ring_buffer_event *event; | ||
347 | struct ftrace_graph_ent *call; | 528 | struct ftrace_graph_ent *call; |
348 | unsigned long long duration; | 529 | unsigned long long duration; |
349 | int ret; | 530 | int ret; |
350 | int i; | 531 | int i; |
351 | 532 | ||
352 | event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL); | ||
353 | ret_entry = ring_buffer_event_data(event); | ||
354 | graph_ret = &ret_entry->ret; | 533 | graph_ret = &ret_entry->ret; |
355 | call = &entry->graph_ent; | 534 | call = &entry->graph_ent; |
356 | duration = graph_ret->rettime - graph_ret->calltime; | 535 | duration = graph_ret->rettime - graph_ret->calltime; |
357 | 536 | ||
358 | /* Overhead */ | 537 | if (data) { |
359 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { | 538 | int cpu = iter->cpu; |
360 | ret = print_graph_overhead(duration, s); | 539 | int *depth = &(per_cpu_ptr(data, cpu)->depth); |
361 | if (!ret) | 540 | |
362 | return TRACE_TYPE_PARTIAL_LINE; | 541 | /* |
542 | * Comments display at + 1 to depth. Since | ||
543 | * this is a leaf function, keep the comments | ||
544 | * equal to this depth. | ||
545 | */ | ||
546 | *depth = call->depth - 1; | ||
363 | } | 547 | } |
364 | 548 | ||
365 | /* Duration */ | 549 | /* Overhead */ |
366 | ret = print_graph_duration(duration, s); | 550 | ret = print_graph_overhead(duration, s); |
367 | if (ret == TRACE_TYPE_PARTIAL_LINE) | 551 | if (!ret) |
368 | return TRACE_TYPE_PARTIAL_LINE; | 552 | return TRACE_TYPE_PARTIAL_LINE; |
369 | 553 | ||
554 | /* Duration */ | ||
555 | if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { | ||
556 | ret = print_graph_duration(duration, s); | ||
557 | if (ret == TRACE_TYPE_PARTIAL_LINE) | ||
558 | return TRACE_TYPE_PARTIAL_LINE; | ||
559 | } | ||
560 | |||
370 | /* Function */ | 561 | /* Function */ |
371 | for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { | 562 | for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { |
372 | ret = trace_seq_printf(s, " "); | 563 | ret = trace_seq_printf(s, " "); |
@@ -386,33 +577,34 @@ print_graph_entry_leaf(struct trace_iterator *iter, | |||
386 | } | 577 | } |
387 | 578 | ||
388 | static enum print_line_t | 579 | static enum print_line_t |
389 | print_graph_entry_nested(struct ftrace_graph_ent_entry *entry, | 580 | print_graph_entry_nested(struct trace_iterator *iter, |
390 | struct trace_seq *s, pid_t pid, int cpu) | 581 | struct ftrace_graph_ent_entry *entry, |
582 | struct trace_seq *s, int cpu) | ||
391 | { | 583 | { |
392 | int i; | ||
393 | int ret; | ||
394 | struct ftrace_graph_ent *call = &entry->graph_ent; | 584 | struct ftrace_graph_ent *call = &entry->graph_ent; |
585 | struct fgraph_data *data = iter->private; | ||
586 | int ret; | ||
587 | int i; | ||
395 | 588 | ||
396 | /* No overhead */ | 589 | if (data) { |
397 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { | 590 | int cpu = iter->cpu; |
398 | ret = trace_seq_printf(s, " "); | 591 | int *depth = &(per_cpu_ptr(data, cpu)->depth); |
399 | if (!ret) | 592 | |
400 | return TRACE_TYPE_PARTIAL_LINE; | 593 | *depth = call->depth; |
401 | } | 594 | } |
402 | 595 | ||
403 | /* Interrupt */ | 596 | /* No overhead */ |
404 | ret = print_graph_irq(s, call->func, TRACE_GRAPH_ENT, cpu, pid); | 597 | ret = print_graph_overhead(-1, s); |
405 | if (ret == TRACE_TYPE_UNHANDLED) { | 598 | if (!ret) |
406 | /* No time */ | 599 | return TRACE_TYPE_PARTIAL_LINE; |
600 | |||
601 | /* No time */ | ||
602 | if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { | ||
407 | ret = trace_seq_printf(s, " | "); | 603 | ret = trace_seq_printf(s, " | "); |
408 | if (!ret) | 604 | if (!ret) |
409 | return TRACE_TYPE_PARTIAL_LINE; | 605 | return TRACE_TYPE_PARTIAL_LINE; |
410 | } else { | ||
411 | if (ret == TRACE_TYPE_PARTIAL_LINE) | ||
412 | return TRACE_TYPE_PARTIAL_LINE; | ||
413 | } | 606 | } |
414 | 607 | ||
415 | |||
416 | /* Function */ | 608 | /* Function */ |
417 | for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { | 609 | for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { |
418 | ret = trace_seq_printf(s, " "); | 610 | ret = trace_seq_printf(s, " "); |
@@ -428,20 +620,40 @@ print_graph_entry_nested(struct ftrace_graph_ent_entry *entry, | |||
428 | if (!ret) | 620 | if (!ret) |
429 | return TRACE_TYPE_PARTIAL_LINE; | 621 | return TRACE_TYPE_PARTIAL_LINE; |
430 | 622 | ||
431 | return TRACE_TYPE_HANDLED; | 623 | /* |
624 | * we already consumed the current entry to check the next one | ||
625 | * and see if this is a leaf. | ||
626 | */ | ||
627 | return TRACE_TYPE_NO_CONSUME; | ||
432 | } | 628 | } |
433 | 629 | ||
434 | static enum print_line_t | 630 | static enum print_line_t |
435 | print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, | 631 | print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s, |
436 | struct trace_iterator *iter, int cpu) | 632 | int type, unsigned long addr) |
437 | { | 633 | { |
438 | int ret; | 634 | struct fgraph_data *data = iter->private; |
439 | struct trace_entry *ent = iter->ent; | 635 | struct trace_entry *ent = iter->ent; |
636 | int cpu = iter->cpu; | ||
637 | int ret; | ||
440 | 638 | ||
441 | /* Pid */ | 639 | /* Pid */ |
442 | if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE) | 640 | if (verif_pid(s, ent->pid, cpu, data) == TRACE_TYPE_PARTIAL_LINE) |
443 | return TRACE_TYPE_PARTIAL_LINE; | 641 | return TRACE_TYPE_PARTIAL_LINE; |
444 | 642 | ||
643 | if (type) { | ||
644 | /* Interrupt */ | ||
645 | ret = print_graph_irq(iter, addr, type, cpu, ent->pid); | ||
646 | if (ret == TRACE_TYPE_PARTIAL_LINE) | ||
647 | return TRACE_TYPE_PARTIAL_LINE; | ||
648 | } | ||
649 | |||
650 | /* Absolute time */ | ||
651 | if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) { | ||
652 | ret = print_graph_abs_time(iter->ts, s); | ||
653 | if (!ret) | ||
654 | return TRACE_TYPE_PARTIAL_LINE; | ||
655 | } | ||
656 | |||
445 | /* Cpu */ | 657 | /* Cpu */ |
446 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { | 658 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { |
447 | ret = print_graph_cpu(s, cpu); | 659 | ret = print_graph_cpu(s, cpu); |
@@ -460,54 +672,65 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, | |||
460 | return TRACE_TYPE_PARTIAL_LINE; | 672 | return TRACE_TYPE_PARTIAL_LINE; |
461 | } | 673 | } |
462 | 674 | ||
463 | if (trace_branch_is_leaf(iter, field)) | 675 | return 0; |
464 | return print_graph_entry_leaf(iter, field, s); | 676 | } |
677 | |||
678 | static enum print_line_t | ||
679 | print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, | ||
680 | struct trace_iterator *iter) | ||
681 | { | ||
682 | int cpu = iter->cpu; | ||
683 | struct ftrace_graph_ent *call = &field->graph_ent; | ||
684 | struct ftrace_graph_ret_entry *leaf_ret; | ||
685 | |||
686 | if (print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func)) | ||
687 | return TRACE_TYPE_PARTIAL_LINE; | ||
688 | |||
689 | leaf_ret = get_return_for_leaf(iter, field); | ||
690 | if (leaf_ret) | ||
691 | return print_graph_entry_leaf(iter, field, leaf_ret, s); | ||
465 | else | 692 | else |
466 | return print_graph_entry_nested(field, s, iter->ent->pid, cpu); | 693 | return print_graph_entry_nested(iter, field, s, cpu); |
467 | 694 | ||
468 | } | 695 | } |
469 | 696 | ||
470 | static enum print_line_t | 697 | static enum print_line_t |
471 | print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | 698 | print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, |
472 | struct trace_entry *ent, int cpu) | 699 | struct trace_entry *ent, struct trace_iterator *iter) |
473 | { | 700 | { |
474 | int i; | ||
475 | int ret; | ||
476 | unsigned long long duration = trace->rettime - trace->calltime; | 701 | unsigned long long duration = trace->rettime - trace->calltime; |
702 | struct fgraph_data *data = iter->private; | ||
703 | pid_t pid = ent->pid; | ||
704 | int cpu = iter->cpu; | ||
705 | int ret; | ||
706 | int i; | ||
477 | 707 | ||
478 | /* Pid */ | 708 | if (data) { |
479 | if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE) | 709 | int cpu = iter->cpu; |
480 | return TRACE_TYPE_PARTIAL_LINE; | 710 | int *depth = &(per_cpu_ptr(data, cpu)->depth); |
481 | 711 | ||
482 | /* Cpu */ | 712 | /* |
483 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { | 713 | * Comments display at + 1 to depth. This is the |
484 | ret = print_graph_cpu(s, cpu); | 714 | * return from a function, we now want the comments |
485 | if (ret == TRACE_TYPE_PARTIAL_LINE) | 715 | * to display at the same level of the bracket. |
486 | return TRACE_TYPE_PARTIAL_LINE; | 716 | */ |
717 | *depth = trace->depth - 1; | ||
487 | } | 718 | } |
488 | 719 | ||
489 | /* Proc */ | 720 | if (print_graph_prologue(iter, s, 0, 0)) |
490 | if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { | 721 | return TRACE_TYPE_PARTIAL_LINE; |
491 | ret = print_graph_proc(s, ent->pid); | ||
492 | if (ret == TRACE_TYPE_PARTIAL_LINE) | ||
493 | return TRACE_TYPE_PARTIAL_LINE; | ||
494 | |||
495 | ret = trace_seq_printf(s, " | "); | ||
496 | if (!ret) | ||
497 | return TRACE_TYPE_PARTIAL_LINE; | ||
498 | } | ||
499 | 722 | ||
500 | /* Overhead */ | 723 | /* Overhead */ |
501 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { | 724 | ret = print_graph_overhead(duration, s); |
502 | ret = print_graph_overhead(duration, s); | 725 | if (!ret) |
503 | if (!ret) | 726 | return TRACE_TYPE_PARTIAL_LINE; |
504 | return TRACE_TYPE_PARTIAL_LINE; | ||
505 | } | ||
506 | 727 | ||
507 | /* Duration */ | 728 | /* Duration */ |
508 | ret = print_graph_duration(duration, s); | 729 | if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { |
509 | if (ret == TRACE_TYPE_PARTIAL_LINE) | 730 | ret = print_graph_duration(duration, s); |
510 | return TRACE_TYPE_PARTIAL_LINE; | 731 | if (ret == TRACE_TYPE_PARTIAL_LINE) |
732 | return TRACE_TYPE_PARTIAL_LINE; | ||
733 | } | ||
511 | 734 | ||
512 | /* Closing brace */ | 735 | /* Closing brace */ |
513 | for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) { | 736 | for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) { |
@@ -528,7 +751,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |||
528 | return TRACE_TYPE_PARTIAL_LINE; | 751 | return TRACE_TYPE_PARTIAL_LINE; |
529 | } | 752 | } |
530 | 753 | ||
531 | ret = print_graph_irq(s, trace->func, TRACE_GRAPH_RET, cpu, ent->pid); | 754 | ret = print_graph_irq(iter, trace->func, TRACE_GRAPH_RET, cpu, pid); |
532 | if (ret == TRACE_TYPE_PARTIAL_LINE) | 755 | if (ret == TRACE_TYPE_PARTIAL_LINE) |
533 | return TRACE_TYPE_PARTIAL_LINE; | 756 | return TRACE_TYPE_PARTIAL_LINE; |
534 | 757 | ||
@@ -536,61 +759,73 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |||
536 | } | 759 | } |
537 | 760 | ||
538 | static enum print_line_t | 761 | static enum print_line_t |
539 | print_graph_comment(struct print_entry *trace, struct trace_seq *s, | 762 | print_graph_comment(struct trace_seq *s, struct trace_entry *ent, |
540 | struct trace_entry *ent, struct trace_iterator *iter) | 763 | struct trace_iterator *iter) |
541 | { | 764 | { |
542 | int i; | 765 | unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); |
766 | struct fgraph_data *data = iter->private; | ||
767 | struct trace_event *event; | ||
768 | int depth = 0; | ||
543 | int ret; | 769 | int ret; |
770 | int i; | ||
544 | 771 | ||
545 | /* Pid */ | 772 | if (data) |
546 | if (verif_pid(s, ent->pid, iter->cpu) == TRACE_TYPE_PARTIAL_LINE) | 773 | depth = per_cpu_ptr(data, iter->cpu)->depth; |
547 | return TRACE_TYPE_PARTIAL_LINE; | ||
548 | |||
549 | /* Cpu */ | ||
550 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { | ||
551 | ret = print_graph_cpu(s, iter->cpu); | ||
552 | if (ret == TRACE_TYPE_PARTIAL_LINE) | ||
553 | return TRACE_TYPE_PARTIAL_LINE; | ||
554 | } | ||
555 | 774 | ||
556 | /* Proc */ | 775 | if (print_graph_prologue(iter, s, 0, 0)) |
557 | if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { | 776 | return TRACE_TYPE_PARTIAL_LINE; |
558 | ret = print_graph_proc(s, ent->pid); | ||
559 | if (ret == TRACE_TYPE_PARTIAL_LINE) | ||
560 | return TRACE_TYPE_PARTIAL_LINE; | ||
561 | |||
562 | ret = trace_seq_printf(s, " | "); | ||
563 | if (!ret) | ||
564 | return TRACE_TYPE_PARTIAL_LINE; | ||
565 | } | ||
566 | 777 | ||
567 | /* No overhead */ | 778 | /* No overhead */ |
568 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { | 779 | ret = print_graph_overhead(-1, s); |
569 | ret = trace_seq_printf(s, " "); | 780 | if (!ret) |
781 | return TRACE_TYPE_PARTIAL_LINE; | ||
782 | |||
783 | /* No time */ | ||
784 | if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { | ||
785 | ret = trace_seq_printf(s, " | "); | ||
570 | if (!ret) | 786 | if (!ret) |
571 | return TRACE_TYPE_PARTIAL_LINE; | 787 | return TRACE_TYPE_PARTIAL_LINE; |
572 | } | 788 | } |
573 | 789 | ||
574 | /* No time */ | ||
575 | ret = trace_seq_printf(s, " | "); | ||
576 | if (!ret) | ||
577 | return TRACE_TYPE_PARTIAL_LINE; | ||
578 | |||
579 | /* Indentation */ | 790 | /* Indentation */ |
580 | if (trace->depth > 0) | 791 | if (depth > 0) |
581 | for (i = 0; i < (trace->depth + 1) * TRACE_GRAPH_INDENT; i++) { | 792 | for (i = 0; i < (depth + 1) * TRACE_GRAPH_INDENT; i++) { |
582 | ret = trace_seq_printf(s, " "); | 793 | ret = trace_seq_printf(s, " "); |
583 | if (!ret) | 794 | if (!ret) |
584 | return TRACE_TYPE_PARTIAL_LINE; | 795 | return TRACE_TYPE_PARTIAL_LINE; |
585 | } | 796 | } |
586 | 797 | ||
587 | /* The comment */ | 798 | /* The comment */ |
588 | ret = trace_seq_printf(s, "/* %s", trace->buf); | 799 | ret = trace_seq_printf(s, "/* "); |
589 | if (!ret) | 800 | if (!ret) |
590 | return TRACE_TYPE_PARTIAL_LINE; | 801 | return TRACE_TYPE_PARTIAL_LINE; |
591 | 802 | ||
592 | if (ent->flags & TRACE_FLAG_CONT) | 803 | switch (iter->ent->type) { |
593 | trace_seq_print_cont(s, iter); | 804 | case TRACE_BPRINT: |
805 | ret = trace_print_bprintk_msg_only(iter); | ||
806 | if (ret != TRACE_TYPE_HANDLED) | ||
807 | return ret; | ||
808 | break; | ||
809 | case TRACE_PRINT: | ||
810 | ret = trace_print_printk_msg_only(iter); | ||
811 | if (ret != TRACE_TYPE_HANDLED) | ||
812 | return ret; | ||
813 | break; | ||
814 | default: | ||
815 | event = ftrace_find_event(ent->type); | ||
816 | if (!event) | ||
817 | return TRACE_TYPE_UNHANDLED; | ||
818 | |||
819 | ret = event->trace(iter, sym_flags); | ||
820 | if (ret != TRACE_TYPE_HANDLED) | ||
821 | return ret; | ||
822 | } | ||
823 | |||
824 | /* Strip ending newline */ | ||
825 | if (s->buffer[s->len - 1] == '\n') { | ||
826 | s->buffer[s->len - 1] = '\0'; | ||
827 | s->len--; | ||
828 | } | ||
594 | 829 | ||
595 | ret = trace_seq_printf(s, " */\n"); | 830 | ret = trace_seq_printf(s, " */\n"); |
596 | if (!ret) | 831 | if (!ret) |
@@ -603,62 +838,98 @@ print_graph_comment(struct print_entry *trace, struct trace_seq *s, | |||
603 | enum print_line_t | 838 | enum print_line_t |
604 | print_graph_function(struct trace_iterator *iter) | 839 | print_graph_function(struct trace_iterator *iter) |
605 | { | 840 | { |
606 | struct trace_seq *s = &iter->seq; | ||
607 | struct trace_entry *entry = iter->ent; | 841 | struct trace_entry *entry = iter->ent; |
842 | struct trace_seq *s = &iter->seq; | ||
608 | 843 | ||
609 | switch (entry->type) { | 844 | switch (entry->type) { |
610 | case TRACE_GRAPH_ENT: { | 845 | case TRACE_GRAPH_ENT: { |
611 | struct ftrace_graph_ent_entry *field; | 846 | /* |
847 | * print_graph_entry() may consume the current event, | ||
848 | * thus @field may become invalid, so we need to save it. | ||
849 | * sizeof(struct ftrace_graph_ent_entry) is very small, | ||
850 | * it can be safely saved at the stack. | ||
851 | */ | ||
852 | struct ftrace_graph_ent_entry *field, saved; | ||
612 | trace_assign_type(field, entry); | 853 | trace_assign_type(field, entry); |
613 | return print_graph_entry(field, s, iter, | 854 | saved = *field; |
614 | iter->cpu); | 855 | return print_graph_entry(&saved, s, iter); |
615 | } | 856 | } |
616 | case TRACE_GRAPH_RET: { | 857 | case TRACE_GRAPH_RET: { |
617 | struct ftrace_graph_ret_entry *field; | 858 | struct ftrace_graph_ret_entry *field; |
618 | trace_assign_type(field, entry); | 859 | trace_assign_type(field, entry); |
619 | return print_graph_return(&field->ret, s, entry, iter->cpu); | 860 | return print_graph_return(&field->ret, s, entry, iter); |
620 | } | ||
621 | case TRACE_PRINT: { | ||
622 | struct print_entry *field; | ||
623 | trace_assign_type(field, entry); | ||
624 | return print_graph_comment(field, s, entry, iter); | ||
625 | } | 861 | } |
626 | default: | 862 | default: |
627 | return TRACE_TYPE_UNHANDLED; | 863 | return print_graph_comment(s, entry, iter); |
628 | } | 864 | } |
865 | |||
866 | return TRACE_TYPE_HANDLED; | ||
629 | } | 867 | } |
630 | 868 | ||
631 | static void print_graph_headers(struct seq_file *s) | 869 | static void print_graph_headers(struct seq_file *s) |
632 | { | 870 | { |
633 | /* 1st line */ | 871 | /* 1st line */ |
634 | seq_printf(s, "# "); | 872 | seq_printf(s, "# "); |
873 | if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) | ||
874 | seq_printf(s, " TIME "); | ||
635 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) | 875 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) |
636 | seq_printf(s, "CPU "); | 876 | seq_printf(s, "CPU"); |
637 | if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) | 877 | if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) |
638 | seq_printf(s, "TASK/PID "); | 878 | seq_printf(s, " TASK/PID "); |
639 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) | 879 | if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) |
640 | seq_printf(s, "OVERHEAD/"); | 880 | seq_printf(s, " DURATION "); |
641 | seq_printf(s, "DURATION FUNCTION CALLS\n"); | 881 | seq_printf(s, " FUNCTION CALLS\n"); |
642 | 882 | ||
643 | /* 2nd line */ | 883 | /* 2nd line */ |
644 | seq_printf(s, "# "); | 884 | seq_printf(s, "# "); |
885 | if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) | ||
886 | seq_printf(s, " | "); | ||
645 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) | 887 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) |
646 | seq_printf(s, "| "); | 888 | seq_printf(s, "| "); |
647 | if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) | 889 | if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) |
648 | seq_printf(s, "| | "); | 890 | seq_printf(s, " | | "); |
649 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { | 891 | if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) |
650 | seq_printf(s, "| "); | 892 | seq_printf(s, " | | "); |
651 | seq_printf(s, "| | | | |\n"); | 893 | seq_printf(s, " | | | |\n"); |
652 | } else | ||
653 | seq_printf(s, " | | | | |\n"); | ||
654 | } | 894 | } |
895 | |||
896 | static void graph_trace_open(struct trace_iterator *iter) | ||
897 | { | ||
898 | /* pid and depth on the last trace processed */ | ||
899 | struct fgraph_data *data = alloc_percpu(struct fgraph_data); | ||
900 | int cpu; | ||
901 | |||
902 | if (!data) | ||
903 | pr_warning("function graph tracer: not enough memory\n"); | ||
904 | else | ||
905 | for_each_possible_cpu(cpu) { | ||
906 | pid_t *pid = &(per_cpu_ptr(data, cpu)->last_pid); | ||
907 | int *depth = &(per_cpu_ptr(data, cpu)->depth); | ||
908 | *pid = -1; | ||
909 | *depth = 0; | ||
910 | } | ||
911 | |||
912 | iter->private = data; | ||
913 | } | ||
914 | |||
915 | static void graph_trace_close(struct trace_iterator *iter) | ||
916 | { | ||
917 | free_percpu(iter->private); | ||
918 | } | ||
919 | |||
655 | static struct tracer graph_trace __read_mostly = { | 920 | static struct tracer graph_trace __read_mostly = { |
656 | .name = "function_graph", | 921 | .name = "function_graph", |
657 | .init = graph_trace_init, | 922 | .open = graph_trace_open, |
658 | .reset = graph_trace_reset, | 923 | .close = graph_trace_close, |
924 | .wait_pipe = poll_wait_pipe, | ||
925 | .init = graph_trace_init, | ||
926 | .reset = graph_trace_reset, | ||
659 | .print_line = print_graph_function, | 927 | .print_line = print_graph_function, |
660 | .print_header = print_graph_headers, | 928 | .print_header = print_graph_headers, |
661 | .flags = &tracer_flags, | 929 | .flags = &tracer_flags, |
930 | #ifdef CONFIG_FTRACE_SELFTEST | ||
931 | .selftest = trace_selftest_startup_function_graph, | ||
932 | #endif | ||
662 | }; | 933 | }; |
663 | 934 | ||
664 | static __init int init_graph_trace(void) | 935 | static __init int init_graph_trace(void) |