aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2009-03-23 23:12:58 -0400
committerSteven Rostedt <srostedt@redhat.com>2009-03-24 23:41:09 -0400
commit0706f1c48ca8a7ab478090b4e38f2e578ae2bfe0 (patch)
tree26b79090f8e7e62796bb871ff089d04799919855
parent493762fc534c71d11d489f872c4b4a2c61173668 (diff)
tracing: adding function timings to function profiler
If the function graph trace is enabled, the function profiler will use it to take the timing of the functions. cat /debug/tracing/trace_stat/functions Function Hit Time -------- --- ---- mwait_idle 127 183028.4 us schedule 26 151997.7 us __schedule 31 151975.1 us sys_wait4 2 74080.53 us do_wait 2 74077.80 us sys_newlstat 138 39929.16 us do_path_lookup 179 39845.79 us vfs_lstat_fd 138 39761.97 us user_path_at 153 39469.58 us path_walk 179 39435.76 us __link_path_walk 189 39143.73 us [...] Note the times are skewed due to the function graph tracer not taking into account schedules. Signed-off-by: Steven Rostedt <srostedt@redhat.com>
-rw-r--r--kernel/trace/ftrace.c93
-rw-r--r--kernel/trace/trace.c11
-rw-r--r--kernel/trace/trace.h3
-rw-r--r--kernel/trace/trace_functions_graph.c17
-rw-r--r--kernel/trace/trace_output.c10
-rw-r--r--kernel/trace/trace_output.h2
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
251struct ftrace_profile_page { 254struct 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 */
311static 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 */
306static int function_stat_cmp(void *p1, void *p2) 325static 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
319static int function_stat_headers(struct seq_file *m) 339static 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
578static int profile_graph_entry(struct ftrace_graph_ent *trace)
579{
580 function_profile_call(trace->func, 0);
581 return 1;
582}
583
584static 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
596static int register_ftrace_profiler(void)
597{
598 return register_ftrace_graph(&profile_graph_return,
599 &profile_graph_entry);
600}
601
602static void unregister_ftrace_profiler(void)
603{
604 unregister_ftrace_graph();
605}
606#else
537static struct ftrace_ops ftrace_profile_ops __read_mostly = 607static struct ftrace_ops ftrace_profile_ops __read_mostly =
538{ 608{
539 .func = function_profile_call, 609 .func = function_profile_call,
540}; 610};
541 611
612static int register_ftrace_profiler(void)
613{
614 return register_ftrace_function(&ftrace_profile_ops);
615}
616
617static void unregister_ftrace_profiler(void)
618{
619 unregister_ftrace_function(&ftrace_profile_ops);
620}
621#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
622
542static ssize_t 623static ssize_t
543ftrace_profile_write(struct file *filp, const char __user *ubuf, 624ftrace_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
405static void
406trace_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
607extern enum print_line_t print_graph_function(struct trace_iterator *iter); 607extern enum print_line_t print_graph_function(struct trace_iterator *iter);
608extern enum print_line_t
609trace_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 */
641static inline enum print_line_t 642static inline enum print_line_t
642print_graph_function(struct trace_iterator *iter) 643print_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
429static enum print_line_t 429enum print_line_t
430print_graph_duration(unsigned long long duration, struct trace_seq *s) 430trace_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
470static enum print_line_t
471print_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
20static int next_event_type = __TRACE_LAST_TYPE + 1; 20static int next_event_type = __TRACE_LAST_TYPE + 1;
21 21
22void 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
22enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter) 32enum 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);
20extern enum print_line_t 20extern enum print_line_t
21trace_print_printk_msg_only(struct trace_iterator *iter); 21trace_print_printk_msg_only(struct trace_iterator *iter);
22 22
23extern void trace_print_seq(struct seq_file *m, struct trace_seq *s);
24
23extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) 25extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
24 __attribute__ ((format (printf, 2, 3))); 26 __attribute__ ((format (printf, 2, 3)));
25extern int 27extern int