diff options
-rw-r--r-- | kernel/trace/ftrace.c | 93 | ||||
-rw-r--r-- | kernel/trace/trace.c | 11 | ||||
-rw-r--r-- | kernel/trace/trace.h | 3 | ||||
-rw-r--r-- | kernel/trace/trace_functions_graph.c | 17 | ||||
-rw-r--r-- | kernel/trace/trace_output.c | 10 | ||||
-rw-r--r-- | kernel/trace/trace_output.h | 2 |
6 files changed, 117 insertions, 19 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: |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 67c6a21dd427..821bf49771d4 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -402,17 +402,6 @@ static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt) | |||
402 | return cnt; | 402 | return cnt; |
403 | } | 403 | } |
404 | 404 | ||
405 | static void | ||
406 | trace_print_seq(struct seq_file *m, struct trace_seq *s) | ||
407 | { | ||
408 | int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len; | ||
409 | |||
410 | s->buffer[len] = 0; | ||
411 | seq_puts(m, s->buffer); | ||
412 | |||
413 | trace_seq_init(s); | ||
414 | } | ||
415 | |||
416 | /** | 405 | /** |
417 | * update_max_tr - snapshot all trace buffers from global_trace to max_tr | 406 | * update_max_tr - snapshot all trace buffers from global_trace to max_tr |
418 | * @tr: tracer | 407 | * @tr: tracer |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index d7410bbb9a80..c66ca3b66050 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -605,6 +605,8 @@ extern unsigned long trace_flags; | |||
605 | /* Standard output formatting function used for function return traces */ | 605 | /* Standard output formatting function used for function return traces */ |
606 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 606 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
607 | extern enum print_line_t print_graph_function(struct trace_iterator *iter); | 607 | extern enum print_line_t print_graph_function(struct trace_iterator *iter); |
608 | extern enum print_line_t | ||
609 | trace_print_graph_duration(unsigned long long duration, struct trace_seq *s); | ||
608 | 610 | ||
609 | #ifdef CONFIG_DYNAMIC_FTRACE | 611 | #ifdef CONFIG_DYNAMIC_FTRACE |
610 | /* TODO: make this variable */ | 612 | /* TODO: make this variable */ |
@@ -636,7 +638,6 @@ static inline int ftrace_graph_addr(unsigned long addr) | |||
636 | return 1; | 638 | return 1; |
637 | } | 639 | } |
638 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 640 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
639 | |||
640 | #else /* CONFIG_FUNCTION_GRAPH_TRACER */ | 641 | #else /* CONFIG_FUNCTION_GRAPH_TRACER */ |
641 | static inline enum print_line_t | 642 | static inline enum print_line_t |
642 | print_graph_function(struct trace_iterator *iter) | 643 | print_graph_function(struct trace_iterator *iter) |
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index d28687e7b3a7..85bba0f018b0 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
@@ -426,8 +426,8 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr, | |||
426 | return TRACE_TYPE_HANDLED; | 426 | return TRACE_TYPE_HANDLED; |
427 | } | 427 | } |
428 | 428 | ||
429 | static enum print_line_t | 429 | enum print_line_t |
430 | print_graph_duration(unsigned long long duration, struct trace_seq *s) | 430 | trace_print_graph_duration(unsigned long long duration, struct trace_seq *s) |
431 | { | 431 | { |
432 | unsigned long nsecs_rem = do_div(duration, 1000); | 432 | unsigned long nsecs_rem = do_div(duration, 1000); |
433 | /* log10(ULONG_MAX) + '\0' */ | 433 | /* log10(ULONG_MAX) + '\0' */ |
@@ -464,12 +464,23 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s) | |||
464 | if (!ret) | 464 | if (!ret) |
465 | return TRACE_TYPE_PARTIAL_LINE; | 465 | return TRACE_TYPE_PARTIAL_LINE; |
466 | } | 466 | } |
467 | return TRACE_TYPE_HANDLED; | ||
468 | } | ||
469 | |||
470 | static enum print_line_t | ||
471 | print_graph_duration(unsigned long long duration, struct trace_seq *s) | ||
472 | { | ||
473 | int ret; | ||
474 | |||
475 | ret = trace_print_graph_duration(duration, s); | ||
476 | if (ret != TRACE_TYPE_HANDLED) | ||
477 | return ret; | ||
467 | 478 | ||
468 | ret = trace_seq_printf(s, "| "); | 479 | ret = trace_seq_printf(s, "| "); |
469 | if (!ret) | 480 | if (!ret) |
470 | return TRACE_TYPE_PARTIAL_LINE; | 481 | return TRACE_TYPE_PARTIAL_LINE; |
471 | return TRACE_TYPE_HANDLED; | ||
472 | 482 | ||
483 | return TRACE_TYPE_HANDLED; | ||
473 | } | 484 | } |
474 | 485 | ||
475 | /* Case of a leaf function on its call entry */ | 486 | /* Case of a leaf function on its call entry */ |
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 19261fdd2455..a3b6e3fd7044 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c | |||
@@ -19,6 +19,16 @@ static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly; | |||
19 | 19 | ||
20 | static int next_event_type = __TRACE_LAST_TYPE + 1; | 20 | static int next_event_type = __TRACE_LAST_TYPE + 1; |
21 | 21 | ||
22 | void trace_print_seq(struct seq_file *m, struct trace_seq *s) | ||
23 | { | ||
24 | int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len; | ||
25 | |||
26 | s->buffer[len] = 0; | ||
27 | seq_puts(m, s->buffer); | ||
28 | |||
29 | trace_seq_init(s); | ||
30 | } | ||
31 | |||
22 | enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter) | 32 | enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter) |
23 | { | 33 | { |
24 | struct trace_seq *s = &iter->seq; | 34 | struct trace_seq *s = &iter->seq; |
diff --git a/kernel/trace/trace_output.h b/kernel/trace/trace_output.h index 35c422fb51a9..1eac2973374e 100644 --- a/kernel/trace/trace_output.h +++ b/kernel/trace/trace_output.h | |||
@@ -20,6 +20,8 @@ trace_print_bprintk_msg_only(struct trace_iterator *iter); | |||
20 | extern enum print_line_t | 20 | extern enum print_line_t |
21 | trace_print_printk_msg_only(struct trace_iterator *iter); | 21 | trace_print_printk_msg_only(struct trace_iterator *iter); |
22 | 22 | ||
23 | extern void trace_print_seq(struct seq_file *m, struct trace_seq *s); | ||
24 | |||
23 | extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) | 25 | extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) |
24 | __attribute__ ((format (printf, 2, 3))); | 26 | __attribute__ ((format (printf, 2, 3))); |
25 | extern int | 27 | extern int |