diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 93 |
1 files changed, 89 insertions, 4 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 24dac448cdc9..a9ccd71fc922 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -33,7 +33,7 @@ | |||
33 | 33 | ||
34 | #include <asm/ftrace.h> | 34 | #include <asm/ftrace.h> |
35 | 35 | ||
36 | #include "trace.h" | 36 | #include "trace_output.h" |
37 | #include "trace_stat.h" | 37 | #include "trace_stat.h" |
38 | 38 | ||
39 | #define FTRACE_WARN_ON(cond) \ | 39 | #define FTRACE_WARN_ON(cond) \ |
@@ -246,6 +246,9 @@ struct ftrace_profile { | |||
246 | struct hlist_node node; | 246 | struct hlist_node node; |
247 | unsigned long ip; | 247 | unsigned long ip; |
248 | unsigned long counter; | 248 | unsigned long counter; |
249 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
250 | unsigned long long time; | ||
251 | #endif | ||
249 | }; | 252 | }; |
250 | 253 | ||
251 | struct ftrace_profile_page { | 254 | struct ftrace_profile_page { |
@@ -303,6 +306,22 @@ static void *function_stat_start(struct tracer_stat *trace) | |||
303 | return function_stat_next(&profile_pages_start->records[0], 0); | 306 | return function_stat_next(&profile_pages_start->records[0], 0); |
304 | } | 307 | } |
305 | 308 | ||
309 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
310 | /* function graph compares on total time */ | ||
311 | static int function_stat_cmp(void *p1, void *p2) | ||
312 | { | ||
313 | struct ftrace_profile *a = p1; | ||
314 | struct ftrace_profile *b = p2; | ||
315 | |||
316 | if (a->time < b->time) | ||
317 | return -1; | ||
318 | if (a->time > b->time) | ||
319 | return 1; | ||
320 | else | ||
321 | return 0; | ||
322 | } | ||
323 | #else | ||
324 | /* not function graph compares against hits */ | ||
306 | static int function_stat_cmp(void *p1, void *p2) | 325 | static int function_stat_cmp(void *p1, void *p2) |
307 | { | 326 | { |
308 | struct ftrace_profile *a = p1; | 327 | struct ftrace_profile *a = p1; |
@@ -315,11 +334,17 @@ static int function_stat_cmp(void *p1, void *p2) | |||
315 | else | 334 | else |
316 | return 0; | 335 | return 0; |
317 | } | 336 | } |
337 | #endif | ||
318 | 338 | ||
319 | static int function_stat_headers(struct seq_file *m) | 339 | static int function_stat_headers(struct seq_file *m) |
320 | { | 340 | { |
341 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
342 | seq_printf(m, " Function Hit Time\n" | ||
343 | " -------- --- ----\n"); | ||
344 | #else | ||
321 | seq_printf(m, " Function Hit\n" | 345 | seq_printf(m, " Function Hit\n" |
322 | " -------- ---\n"); | 346 | " -------- ---\n"); |
347 | #endif | ||
323 | return 0; | 348 | return 0; |
324 | } | 349 | } |
325 | 350 | ||
@@ -327,10 +352,25 @@ static int function_stat_show(struct seq_file *m, void *v) | |||
327 | { | 352 | { |
328 | struct ftrace_profile *rec = v; | 353 | struct ftrace_profile *rec = v; |
329 | char str[KSYM_SYMBOL_LEN]; | 354 | char str[KSYM_SYMBOL_LEN]; |
355 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
356 | static struct trace_seq s; | ||
357 | static DEFINE_MUTEX(mutex); | ||
358 | |||
359 | mutex_lock(&mutex); | ||
360 | trace_seq_init(&s); | ||
361 | trace_print_graph_duration(rec->time, &s); | ||
362 | #endif | ||
330 | 363 | ||
331 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | 364 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); |
365 | seq_printf(m, " %-30.30s %10lu", str, rec->counter); | ||
366 | |||
367 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
368 | seq_printf(m, " "); | ||
369 | trace_print_seq(m, &s); | ||
370 | mutex_unlock(&mutex); | ||
371 | #endif | ||
372 | seq_putc(m, '\n'); | ||
332 | 373 | ||
333 | seq_printf(m, " %-30.30s %10lu\n", str, rec->counter); | ||
334 | return 0; | 374 | return 0; |
335 | } | 375 | } |
336 | 376 | ||
@@ -534,11 +574,52 @@ function_profile_call(unsigned long ip, unsigned long parent_ip) | |||
534 | local_irq_restore(flags); | 574 | local_irq_restore(flags); |
535 | } | 575 | } |
536 | 576 | ||
577 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
578 | static int profile_graph_entry(struct ftrace_graph_ent *trace) | ||
579 | { | ||
580 | function_profile_call(trace->func, 0); | ||
581 | return 1; | ||
582 | } | ||
583 | |||
584 | static void profile_graph_return(struct ftrace_graph_ret *trace) | ||
585 | { | ||
586 | unsigned long flags; | ||
587 | struct ftrace_profile *rec; | ||
588 | |||
589 | local_irq_save(flags); | ||
590 | rec = ftrace_find_profiled_func(trace->func); | ||
591 | if (rec) | ||
592 | rec->time += trace->rettime - trace->calltime; | ||
593 | local_irq_restore(flags); | ||
594 | } | ||
595 | |||
596 | static int register_ftrace_profiler(void) | ||
597 | { | ||
598 | return register_ftrace_graph(&profile_graph_return, | ||
599 | &profile_graph_entry); | ||
600 | } | ||
601 | |||
602 | static void unregister_ftrace_profiler(void) | ||
603 | { | ||
604 | unregister_ftrace_graph(); | ||
605 | } | ||
606 | #else | ||
537 | static struct ftrace_ops ftrace_profile_ops __read_mostly = | 607 | static struct ftrace_ops ftrace_profile_ops __read_mostly = |
538 | { | 608 | { |
539 | .func = function_profile_call, | 609 | .func = function_profile_call, |
540 | }; | 610 | }; |
541 | 611 | ||
612 | static int register_ftrace_profiler(void) | ||
613 | { | ||
614 | return register_ftrace_function(&ftrace_profile_ops); | ||
615 | } | ||
616 | |||
617 | static void unregister_ftrace_profiler(void) | ||
618 | { | ||
619 | unregister_ftrace_function(&ftrace_profile_ops); | ||
620 | } | ||
621 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
622 | |||
542 | static ssize_t | 623 | static ssize_t |
543 | ftrace_profile_write(struct file *filp, const char __user *ubuf, | 624 | ftrace_profile_write(struct file *filp, const char __user *ubuf, |
544 | size_t cnt, loff_t *ppos) | 625 | size_t cnt, loff_t *ppos) |
@@ -570,11 +651,15 @@ ftrace_profile_write(struct file *filp, const char __user *ubuf, | |||
570 | goto out; | 651 | goto out; |
571 | } | 652 | } |
572 | 653 | ||
573 | register_ftrace_function(&ftrace_profile_ops); | 654 | ret = register_ftrace_profiler(); |
655 | if (ret < 0) { | ||
656 | cnt = ret; | ||
657 | goto out; | ||
658 | } | ||
574 | ftrace_profile_enabled = 1; | 659 | ftrace_profile_enabled = 1; |
575 | } else { | 660 | } else { |
576 | ftrace_profile_enabled = 0; | 661 | ftrace_profile_enabled = 0; |
577 | unregister_ftrace_function(&ftrace_profile_ops); | 662 | unregister_ftrace_profiler(); |
578 | } | 663 | } |
579 | } | 664 | } |
580 | out: | 665 | out: |