Commit 0706f1c4 authored by Steven Rostedt's avatar Steven Rostedt

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: default avatarSteven Rostedt <srostedt@redhat.com>
parent 493762fc
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
#include <asm/ftrace.h> #include <asm/ftrace.h>
#include "trace.h" #include "trace_output.h"
#include "trace_stat.h" #include "trace_stat.h"
#define FTRACE_WARN_ON(cond) \ #define FTRACE_WARN_ON(cond) \
...@@ -246,6 +246,9 @@ struct ftrace_profile { ...@@ -246,6 +246,9 @@ struct ftrace_profile {
struct hlist_node node; struct hlist_node node;
unsigned long ip; unsigned long ip;
unsigned long counter; unsigned long counter;
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
unsigned long long time;
#endif
}; };
struct ftrace_profile_page { struct ftrace_profile_page {
...@@ -303,6 +306,22 @@ static void *function_stat_start(struct tracer_stat *trace) ...@@ -303,6 +306,22 @@ static void *function_stat_start(struct tracer_stat *trace)
return function_stat_next(&profile_pages_start->records[0], 0); return function_stat_next(&profile_pages_start->records[0], 0);
} }
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/* function graph compares on total time */
static int function_stat_cmp(void *p1, void *p2)
{
struct ftrace_profile *a = p1;
struct ftrace_profile *b = p2;
if (a->time < b->time)
return -1;
if (a->time > b->time)
return 1;
else
return 0;
}
#else
/* not function graph compares against hits */
static int function_stat_cmp(void *p1, void *p2) static int function_stat_cmp(void *p1, void *p2)
{ {
struct ftrace_profile *a = p1; struct ftrace_profile *a = p1;
...@@ -315,11 +334,17 @@ static int function_stat_cmp(void *p1, void *p2) ...@@ -315,11 +334,17 @@ static int function_stat_cmp(void *p1, void *p2)
else else
return 0; return 0;
} }
#endif
static int function_stat_headers(struct seq_file *m) static int function_stat_headers(struct seq_file *m)
{ {
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
seq_printf(m, " Function Hit Time\n"
" -------- --- ----\n");
#else
seq_printf(m, " Function Hit\n" seq_printf(m, " Function Hit\n"
" -------- ---\n"); " -------- ---\n");
#endif
return 0; return 0;
} }
...@@ -327,10 +352,25 @@ static int function_stat_show(struct seq_file *m, void *v) ...@@ -327,10 +352,25 @@ static int function_stat_show(struct seq_file *m, void *v)
{ {
struct ftrace_profile *rec = v; struct ftrace_profile *rec = v;
char str[KSYM_SYMBOL_LEN]; char str[KSYM_SYMBOL_LEN];
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
static struct trace_seq s;
static DEFINE_MUTEX(mutex);
mutex_lock(&mutex);
trace_seq_init(&s);
trace_print_graph_duration(rec->time, &s);
#endif
kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
seq_printf(m, " %-30.30s %10lu", str, rec->counter);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
seq_printf(m, " ");
trace_print_seq(m, &s);
mutex_unlock(&mutex);
#endif
seq_putc(m, '\n');
seq_printf(m, " %-30.30s %10lu\n", str, rec->counter);
return 0; return 0;
} }
...@@ -534,11 +574,52 @@ function_profile_call(unsigned long ip, unsigned long parent_ip) ...@@ -534,11 +574,52 @@ function_profile_call(unsigned long ip, unsigned long parent_ip)
local_irq_restore(flags); local_irq_restore(flags);
} }
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
static int profile_graph_entry(struct ftrace_graph_ent *trace)
{
function_profile_call(trace->func, 0);
return 1;
}
static void profile_graph_return(struct ftrace_graph_ret *trace)
{
unsigned long flags;
struct ftrace_profile *rec;
local_irq_save(flags);
rec = ftrace_find_profiled_func(trace->func);
if (rec)
rec->time += trace->rettime - trace->calltime;
local_irq_restore(flags);
}
static int register_ftrace_profiler(void)
{
return register_ftrace_graph(&profile_graph_return,
&profile_graph_entry);
}
static void unregister_ftrace_profiler(void)
{
unregister_ftrace_graph();
}
#else
static struct ftrace_ops ftrace_profile_ops __read_mostly = static struct ftrace_ops ftrace_profile_ops __read_mostly =
{ {
.func = function_profile_call, .func = function_profile_call,
}; };
static int register_ftrace_profiler(void)
{
return register_ftrace_function(&ftrace_profile_ops);
}
static void unregister_ftrace_profiler(void)
{
unregister_ftrace_function(&ftrace_profile_ops);
}
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
static ssize_t static ssize_t
ftrace_profile_write(struct file *filp, const char __user *ubuf, ftrace_profile_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos) size_t cnt, loff_t *ppos)
...@@ -570,11 +651,15 @@ ftrace_profile_write(struct file *filp, const char __user *ubuf, ...@@ -570,11 +651,15 @@ ftrace_profile_write(struct file *filp, const char __user *ubuf,
goto out; goto out;
} }
register_ftrace_function(&ftrace_profile_ops); ret = register_ftrace_profiler();
if (ret < 0) {
cnt = ret;
goto out;
}
ftrace_profile_enabled = 1; ftrace_profile_enabled = 1;
} else { } else {
ftrace_profile_enabled = 0; ftrace_profile_enabled = 0;
unregister_ftrace_function(&ftrace_profile_ops); unregister_ftrace_profiler();
} }
} }
out: out:
......
...@@ -402,17 +402,6 @@ static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt) ...@@ -402,17 +402,6 @@ static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt)
return cnt; return cnt;
} }
static void
trace_print_seq(struct seq_file *m, struct trace_seq *s)
{
int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len;
s->buffer[len] = 0;
seq_puts(m, s->buffer);
trace_seq_init(s);
}
/** /**
* update_max_tr - snapshot all trace buffers from global_trace to max_tr * update_max_tr - snapshot all trace buffers from global_trace to max_tr
* @tr: tracer * @tr: tracer
......
...@@ -605,6 +605,8 @@ extern unsigned long trace_flags; ...@@ -605,6 +605,8 @@ extern unsigned long trace_flags;
/* Standard output formatting function used for function return traces */ /* Standard output formatting function used for function return traces */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
extern enum print_line_t print_graph_function(struct trace_iterator *iter); extern enum print_line_t print_graph_function(struct trace_iterator *iter);
extern enum print_line_t
trace_print_graph_duration(unsigned long long duration, struct trace_seq *s);
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
/* TODO: make this variable */ /* TODO: make this variable */
...@@ -636,7 +638,6 @@ static inline int ftrace_graph_addr(unsigned long addr) ...@@ -636,7 +638,6 @@ static inline int ftrace_graph_addr(unsigned long addr)
return 1; return 1;
} }
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
#else /* CONFIG_FUNCTION_GRAPH_TRACER */ #else /* CONFIG_FUNCTION_GRAPH_TRACER */
static inline enum print_line_t static inline enum print_line_t
print_graph_function(struct trace_iterator *iter) print_graph_function(struct trace_iterator *iter)
......
...@@ -426,8 +426,8 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr, ...@@ -426,8 +426,8 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr,
return TRACE_TYPE_HANDLED; return TRACE_TYPE_HANDLED;
} }
static enum print_line_t enum print_line_t
print_graph_duration(unsigned long long duration, struct trace_seq *s) trace_print_graph_duration(unsigned long long duration, struct trace_seq *s)
{ {
unsigned long nsecs_rem = do_div(duration, 1000); unsigned long nsecs_rem = do_div(duration, 1000);
/* log10(ULONG_MAX) + '\0' */ /* log10(ULONG_MAX) + '\0' */
...@@ -464,12 +464,23 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s) ...@@ -464,12 +464,23 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s)
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
return TRACE_TYPE_HANDLED;
}
static enum print_line_t
print_graph_duration(unsigned long long duration, struct trace_seq *s)
{
int ret;
ret = trace_print_graph_duration(duration, s);
if (ret != TRACE_TYPE_HANDLED)
return ret;
ret = trace_seq_printf(s, "| "); ret = trace_seq_printf(s, "| ");
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
return TRACE_TYPE_HANDLED;
return TRACE_TYPE_HANDLED;
} }
/* Case of a leaf function on its call entry */ /* Case of a leaf function on its call entry */
......
...@@ -19,6 +19,16 @@ static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly; ...@@ -19,6 +19,16 @@ static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly;
static int next_event_type = __TRACE_LAST_TYPE + 1; static int next_event_type = __TRACE_LAST_TYPE + 1;
void trace_print_seq(struct seq_file *m, struct trace_seq *s)
{
int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len;
s->buffer[len] = 0;
seq_puts(m, s->buffer);
trace_seq_init(s);
}
enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter) enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter)
{ {
struct trace_seq *s = &iter->seq; struct trace_seq *s = &iter->seq;
......
...@@ -20,6 +20,8 @@ trace_print_bprintk_msg_only(struct trace_iterator *iter); ...@@ -20,6 +20,8 @@ trace_print_bprintk_msg_only(struct trace_iterator *iter);
extern enum print_line_t extern enum print_line_t
trace_print_printk_msg_only(struct trace_iterator *iter); trace_print_printk_msg_only(struct trace_iterator *iter);
extern void trace_print_seq(struct seq_file *m, struct trace_seq *s);
extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3))); __attribute__ ((format (printf, 2, 3)));
extern int extern int
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment