/* * Infrastructure for profiling code inserted by 'gcc -pg'. * * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com> * Copyright (C) 2004-2008 Ingo Molnar <mingo@redhat.com> * * Originally ported from the -rt patch by: * Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com> * * Based on code in the latency_tracer, that is: * * Copyright (C) 2004-2006 Ingo Molnar * Copyright (C) 2004 William Lee Irwin III */ #include <linux/stop_machine.h>#include <linux/clocksource.h>#include <linux/kallsyms.h>#include <linux/seq_file.h>#include <linux/suspend.h>#include <linux/debugfs.h>#include <linux/hardirq.h>#include <linux/kthread.h>#include <linux/uaccess.h>#include <linux/module.h>#include <linux/ftrace.h>#include <linux/sysctl.h>#include <linux/slab.h>#include <linux/ctype.h>#include <linux/list.h>#include <linux/hash.h>#include <linux/rcupdate.h> #include <trace/events/sched.h> #include <asm/setup.h> #include "trace_output.h"#include "trace_stat.h" #define FTRACE_WARN_ON(cond) \ ({ \ int ___r = cond; \ if (WARN_ON(___r)) \ ftrace_kill(); \ ___r; \ }) #define FTRACE_WARN_ON_ONCE(cond) \ ({ \ int ___r = cond; \ if (WARN_ON_ONCE(___r)) \ ftrace_kill(); \ ___r; \ }) /* hash bits for specific function selection */#define FTRACE_HASH_BITS 7#define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS)#define FTRACE_HASH_DEFAULT_BITS 10#define FTRACE_HASH_MAX_BITS 12 /* ftrace_enabled is a method to turn ftrace on or off */int ftrace_enabled __read_mostly;static int last_ftrace_enabled; /* Quick disabling of function tracer. */int function_trace_stop; /* List for set_ftrace_pid's pids. */LIST_HEAD(ftrace_pids);struct ftrace_pid { struct list_head list; struct pid *pid;}; /* * ftrace_disabled is set when an anomaly is discovered. * ftrace_disabled is much stronger than ftrace_enabled. */static int ftrace_disabled __read_mostly; static DEFINE_MUTEX(ftrace_lock); static struct ftrace_ops ftrace_list_end __read_mostly = { .func = ftrace_stub,}; static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end;static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub;ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;static struct ftrace_ops global_ops; static voidftrace_ops_list_func(unsigned long ip, unsigned long parent_ip); /* * Traverse the ftrace_global_list, invoking all entries. The reason that we * can use rcu_dereference_raw() is that elements removed from this list * are simply leaked, so there is no need to interact with a grace-period * mechanism. The rcu_dereference_raw() calls are needed to handle * concurrent insertions into the ftrace_global_list. * * Silly Alpha and silly pointer-speculation compiler optimizations! */static void ftrace_global_list_func(unsigned long ip, unsigned long parent_ip){ struct ftrace_ops *op; if (unlikely(trace_recursion_test(TRACE_GLOBAL_BIT))) return; trace_recursion_set(TRACE_GLOBAL_BIT); op = rcu_dereference_raw(ftrace_global_list); /*see above*/ while (op != &ftrace_list_end) { op->func(ip, parent_ip); op = rcu_dereference_raw(op->next); /*see above*/ }; trace_recursion_clear(TRACE_GLOBAL_BIT);} static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip){ if (!test_tsk_trace_trace(current)) return; ftrace_pid_function(ip, parent_ip);} static void set_ftrace_pid_function(ftrace_func_t func){ /* do not set ftrace_pid_function to itself! */ if (func != ftrace_pid_func) ftrace_pid_function = func;} /** * clear_ftrace_function - reset the ftrace function * * This NULLs the ftrace function and in essence stops * tracing. There may be lag */void clear_ftrace_function(void){ ftrace_trace_function = ftrace_stub; __ftrace_trace_function = ftrace_stub; __ftrace_trace_function_delay = ftrace_stub; ftrace_pid_function = ftrace_stub;} #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST/* * For those archs that do not test ftrace_trace_stop in their * mcount call site, we need to do it from C. */static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip){ if (function_trace_stop) return; __ftrace_trace_function(ip, parent_ip);}#endif static void update_global_ops(void){ ftrace_func_t func; /* * If there's only one function registered, then call that * function directly. Otherwise, we need to iterate over the * registered callers. */ if (ftrace_global_list == &ftrace_list_end || ftrace_global_list->next == &ftrace_list_end) func = ftrace_global_list->func; else func = ftrace_global_list_func; /* If we filter on pids, update to use the pid function */ if (!list_empty(&ftrace_pids)) { set_ftrace_pid_function(func); func = ftrace_pid_func; } global_ops.func = func;} static void update_ftrace_function(void){ ftrace_func_t func; update_global_ops(); /* * If we are at the end of the list and this ops is * not dynamic, then have the mcount trampoline call * the function directly */ if (ftrace_ops_list == &ftrace_list_end || (ftrace_ops_list->next == &ftrace_list_end && !(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC))) func = ftrace_ops_list->func; else func = ftrace_ops_list_func; #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST ftrace_trace_function = func;#else#ifdef CONFIG_DYNAMIC_FTRACE /* do not update till all functions have been modified */ __ftrace_trace_function_delay = func;#else __ftrace_trace_function = func;#endif ftrace_trace_function = ftrace_test_stop_func;#endif} static void add_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops){ ops->next = *list; /* * We are entering ops into the list but another * CPU might be walking that list. We need to make sure * the ops->next pointer is valid before another CPU sees * the ops pointer included into the list. */ rcu_assign_pointer(*list, ops);} static int remove_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops){ struct ftrace_ops **p; /* * If we are removing the last function, then simply point * to the ftrace_stub. */ if (*list == ops && ops->next == &ftrace_list_end) { *list = &ftrace_list_end; return 0; } for (p = list; *p != &ftrace_list_end; p = &(*p)->next) if (*p == ops) break; if (*p != ops) return -1; *p = (*p)->next; return 0;} static int __register_ftrace_function(struct ftrace_ops *ops){ if (ftrace_disabled) return -ENODEV; if (FTRACE_WARN_ON(ops == &global_ops)) return -EINVAL; if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED)) return -EBUSY; if (!core_kernel_data((unsigned long)ops)) ops->flags |= FTRACE_OPS_FL_DYNAMIC; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { int first = ftrace_global_list == &ftrace_list_end; add_ftrace_ops(&ftrace_global_list, ops); ops->flags |= FTRACE_OPS_FL_ENABLED; if (first) add_ftrace_ops(&ftrace_ops_list, &global_ops); } else add_ftrace_ops(&ftrace_ops_list, ops); if (ftrace_enabled) update_ftrace_function(); return 0;} static int __unregister_ftrace_function(struct ftrace_ops *ops){ int ret; if (ftrace_disabled) return -ENODEV; if (WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED))) return -EBUSY; if (FTRACE_WARN_ON(ops == &global_ops)) return -EINVAL; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { ret = remove_ftrace_ops(&ftrace_global_list, ops); if (!ret && ftrace_global_list == &ftrace_list_end) ret = remove_ftrace_ops(&ftrace_ops_list, &global_ops); if (!ret) ops->flags &= ~FTRACE_OPS_FL_ENABLED; } else ret = remove_ftrace_ops(&ftrace_ops_list, ops); if (ret < 0) return ret; if (ftrace_enabled) update_ftrace_function(); /* * Dynamic ops may be freed, we must make sure that all * callers are done before leaving this function. */ if (ops->flags & FTRACE_OPS_FL_DYNAMIC) synchronize_sched(); return 0;} static void ftrace_update_pid_func(void){ /* Only do something if we are tracing something */ if (ftrace_trace_function == ftrace_stub) return; update_ftrace_function();} #ifdef CONFIG_FUNCTION_PROFILERstruct ftrace_profile { struct hlist_node node; unsigned long ip; unsigned long counter;#ifdef CONFIG_FUNCTION_GRAPH_TRACER unsigned long long time; unsigned long long time_squared;#endif}; struct ftrace_profile_page { struct ftrace_profile_page *next; unsigned long index; struct ftrace_profile records[];}; struct ftrace_profile_stat { atomic_t disabled; struct hlist_head *hash; struct ftrace_profile_page *pages; struct ftrace_profile_page *start; struct tracer_stat stat;}; #define PROFILE_RECORDS_SIZE \ (PAGE_SIZE - offsetof(struct ftrace_profile_page, records)) #define PROFILES_PER_PAGE \ (PROFILE_RECORDS_SIZE / sizeof(struct ftrace_profile)) static int ftrace_profile_bits __read_mostly;static int ftrace_profile_enabled __read_mostly; /* ftrace_profile_lock - synchronize the enable and disable of the profiler */static DEFINE_MUTEX(ftrace_profile_lock); static DEFINE_PER_CPU(struct ftrace_profile_stat, ftrace_profile_stats); #define FTRACE_PROFILE_HASH_SIZE 1024 /* must be power of 2 */ static void *function_stat_next(void *v, int idx){ struct ftrace_profile *rec = v; struct ftrace_profile_page *pg; pg = (struct ftrace_profile_page *)((unsigned long)rec & PAGE_MASK); again: if (idx != 0) rec++; if ((void *)rec >= (void *)&pg->records[pg->index]) { pg = pg->next; if (!pg) return NULL; rec = &pg->records[0]; if (!rec->counter) goto again; } return rec;} static void *function_stat_start(struct tracer_stat *trace){ struct ftrace_profile_stat *stat = container_of(trace, struct ftrace_profile_stat, stat); if (!stat || !stat->start) return NULL; return function_stat_next(&stat->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){ struct ftrace_profile *a = p1; struct ftrace_profile *b = p2; if (a->counter < b->counter) return -1; if (a->counter > b->counter) return 1; else return 0;}#endif static int function_stat_headers(struct seq_file *m){#ifdef CONFIG_FUNCTION_GRAPH_TRACER seq_printf(m, " Function " "Hit Time Avg s^2\n" " -------- " "--- ---- --- ---\n");#else seq_printf(m, " Function Hit\n" " -------- ---\n");#endif return 0;} static int function_stat_show(struct seq_file *m, void *v){ struct ftrace_profile *rec = v; char str[KSYM_SYMBOL_LEN]; int ret = 0;#ifdef CONFIG_FUNCTION_GRAPH_TRACER static struct trace_seq s; unsigned long long avg; unsigned long long stddev;#endif mutex_lock(&ftrace_profile_lock); /* we raced with function_profile_reset() */ if (unlikely(rec->counter == 0)) { ret = -EBUSY; goto out; } 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, " "); avg = rec->time; do_div(avg, rec->counter); /* Sample standard deviation (s^2) */ if (rec->counter <= 1) stddev = 0; else { stddev = rec->time_squared - rec->counter * avg * avg; /* * Divide only 1000 for ns^2 -> us^2 conversion. * trace_print_graph_duration will divide 1000 again. */ do_div(stddev, (rec->counter - 1) * 1000); } trace_seq_init(&s); trace_print_graph_duration(rec->time, &s); trace_seq_puts(&s, " "); trace_print_graph_duration(avg, &s); trace_seq_puts(&s, " "); trace_print_graph_duration(stddev, &s); trace_print_seq(m, &s);Lines 1–500 of 4245. Load more
/* * Infrastructure for profiling code inserted by 'gcc -pg'. * * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com> * Copyright (C) 2004-2008 Ingo Molnar <mingo@redhat.com> * * Originally ported from the -rt patch by: * Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com> * * Based on code in the latency_tracer, that is: * * Copyright (C) 2004-2006 Ingo Molnar * Copyright (C) 2004 William Lee Irwin III */ #include <linux/stop_machine.h> #include <linux/clocksource.h> #include <linux/kallsyms.h> #include <linux/seq_file.h> #include <linux/suspend.h> #include <linux/debugfs.h> #include <linux/hardirq.h> #include <linux/kthread.h> #include <linux/uaccess.h> #include <linux/module.h> #include <linux/ftrace.h> #include <linux/sysctl.h> #include <linux/slab.h> #include <linux/ctype.h> #include <linux/list.h> #include <linux/hash.h> #include <linux/rcupdate.h> #include <trace/events/sched.h> #include <asm/setup.h> #include "trace_output.h" #include "trace_stat.h" #define FTRACE_WARN_ON(cond) \ ({ \ int ___r = cond; \ if (WARN_ON(___r)) \ ftrace_kill(); \ ___r; \ }) #define FTRACE_WARN_ON_ONCE(cond) \ ({ \ int ___r = cond; \ if (WARN_ON_ONCE(___r)) \ ftrace_kill(); \ ___r; \ }) /* hash bits for specific function selection */ #define FTRACE_HASH_BITS 7 #define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS) #define FTRACE_HASH_DEFAULT_BITS 10 #define FTRACE_HASH_MAX_BITS 12 /* ftrace_enabled is a method to turn ftrace on or off */ int ftrace_enabled __read_mostly; static int last_ftrace_enabled; /* Quick disabling of function tracer. */ int function_trace_stop; /* List for set_ftrace_pid's pids. */ LIST_HEAD(ftrace_pids); struct ftrace_pid { struct list_head list; struct pid *pid; }; /* * ftrace_disabled is set when an anomaly is discovered. * ftrace_disabled is much stronger than ftrace_enabled. */ static int ftrace_disabled __read_mostly; static DEFINE_MUTEX(ftrace_lock); static struct ftrace_ops ftrace_list_end __read_mostly = { .func = ftrace_stub, }; static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end; ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub; ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; static struct ftrace_ops global_ops; static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip); /* * Traverse the ftrace_global_list, invoking all entries. The reason that we * can use rcu_dereference_raw() is that elements removed from this list * are simply leaked, so there is no need to interact with a grace-period * mechanism. The rcu_dereference_raw() calls are needed to handle * concurrent insertions into the ftrace_global_list. * * Silly Alpha and silly pointer-speculation compiler optimizations! */ static void ftrace_global_list_func(unsigned long ip, unsigned long parent_ip) { struct ftrace_ops *op; if (unlikely(trace_recursion_test(TRACE_GLOBAL_BIT))) return; trace_recursion_set(TRACE_GLOBAL_BIT); op = rcu_dereference_raw(ftrace_global_list); /*see above*/ while (op != &ftrace_list_end) { op->func(ip, parent_ip); op = rcu_dereference_raw(op->next); /*see above*/ }; trace_recursion_clear(TRACE_GLOBAL_BIT); } static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip) { if (!test_tsk_trace_trace(current)) return; ftrace_pid_function(ip, parent_ip); } static void set_ftrace_pid_function(ftrace_func_t func) { /* do not set ftrace_pid_function to itself! */ if (func != ftrace_pid_func) ftrace_pid_function = func; } /** * clear_ftrace_function - reset the ftrace function * * This NULLs the ftrace function and in essence stops * tracing. There may be lag */ void clear_ftrace_function(void) { ftrace_trace_function = ftrace_stub; __ftrace_trace_function = ftrace_stub; __ftrace_trace_function_delay = ftrace_stub; ftrace_pid_function = ftrace_stub; } #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST /* * For those archs that do not test ftrace_trace_stop in their * mcount call site, we need to do it from C. */ static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip) { if (function_trace_stop) return; __ftrace_trace_function(ip, parent_ip); } #endif static void update_global_ops(void) { ftrace_func_t func; /* * If there's only one function registered, then call that * function directly. Otherwise, we need to iterate over the * registered callers. */ if (ftrace_global_list == &ftrace_list_end || ftrace_global_list->next == &ftrace_list_end) func = ftrace_global_list->func; else func = ftrace_global_list_func; /* If we filter on pids, update to use the pid function */ if (!list_empty(&ftrace_pids)) { set_ftrace_pid_function(func); func = ftrace_pid_func; } global_ops.func = func; } static void update_ftrace_function(void) { ftrace_func_t func; update_global_ops(); /* * If we are at the end of the list and this ops is * not dynamic, then have the mcount trampoline call * the function directly */ if (ftrace_ops_list == &ftrace_list_end || (ftrace_ops_list->next == &ftrace_list_end && !(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC))) func = ftrace_ops_list->func; else func = ftrace_ops_list_func; #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST ftrace_trace_function = func; #else #ifdef CONFIG_DYNAMIC_FTRACE /* do not update till all functions have been modified */ __ftrace_trace_function_delay = func; #else __ftrace_trace_function = func; #endif ftrace_trace_function = ftrace_test_stop_func; #endif } static void add_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops) { ops->next = *list; /* * We are entering ops into the list but another * CPU might be walking that list. We need to make sure * the ops->next pointer is valid before another CPU sees * the ops pointer included into the list. */ rcu_assign_pointer(*list, ops); } static int remove_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops) { struct ftrace_ops **p; /* * If we are removing the last function, then simply point * to the ftrace_stub. */ if (*list == ops && ops->next == &ftrace_list_end) { *list = &ftrace_list_end; return 0; } for (p = list; *p != &ftrace_list_end; p = &(*p)->next) if (*p == ops) break; if (*p != ops) return -1; *p = (*p)->next; return 0; } static int __register_ftrace_function(struct ftrace_ops *ops) { if (ftrace_disabled) return -ENODEV; if (FTRACE_WARN_ON(ops == &global_ops)) return -EINVAL; if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED)) return -EBUSY; if (!core_kernel_data((unsigned long)ops)) ops->flags |= FTRACE_OPS_FL_DYNAMIC; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { int first = ftrace_global_list == &ftrace_list_end; add_ftrace_ops(&ftrace_global_list, ops); ops->flags |= FTRACE_OPS_FL_ENABLED; if (first) add_ftrace_ops(&ftrace_ops_list, &global_ops); } else add_ftrace_ops(&ftrace_ops_list, ops); if (ftrace_enabled) update_ftrace_function(); return 0; } static int __unregister_ftrace_function(struct ftrace_ops *ops) { int ret; if (ftrace_disabled) return -ENODEV; if (WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED))) return -EBUSY; if (FTRACE_WARN_ON(ops == &global_ops)) return -EINVAL; if (ops->flags & FTRACE_OPS_FL_GLOBAL) { ret = remove_ftrace_ops(&ftrace_global_list, ops); if (!ret && ftrace_global_list == &ftrace_list_end) ret = remove_ftrace_ops(&ftrace_ops_list, &global_ops); if (!ret) ops->flags &= ~FTRACE_OPS_FL_ENABLED; } else ret = remove_ftrace_ops(&ftrace_ops_list, ops); if (ret < 0) return ret; if (ftrace_enabled) update_ftrace_function(); /* * Dynamic ops may be freed, we must make sure that all * callers are done before leaving this function. */ if (ops->flags & FTRACE_OPS_FL_DYNAMIC) synchronize_sched(); return 0; } static void ftrace_update_pid_func(void) { /* Only do something if we are tracing something */ if (ftrace_trace_function == ftrace_stub) return; update_ftrace_function(); } #ifdef CONFIG_FUNCTION_PROFILER struct ftrace_profile { struct hlist_node node; unsigned long ip; unsigned long counter; #ifdef CONFIG_FUNCTION_GRAPH_TRACER unsigned long long time; unsigned long long time_squared; #endif }; struct ftrace_profile_page { struct ftrace_profile_page *next; unsigned long index; struct ftrace_profile records[]; }; struct ftrace_profile_stat { atomic_t disabled; struct hlist_head *hash; struct ftrace_profile_page *pages; struct ftrace_profile_page *start; struct tracer_stat stat; }; #define PROFILE_RECORDS_SIZE \ (PAGE_SIZE - offsetof(struct ftrace_profile_page, records)) #define PROFILES_PER_PAGE \ (PROFILE_RECORDS_SIZE / sizeof(struct ftrace_profile)) static int ftrace_profile_bits __read_mostly; static int ftrace_profile_enabled __read_mostly; /* ftrace_profile_lock - synchronize the enable and disable of the profiler */ static DEFINE_MUTEX(ftrace_profile_lock); static DEFINE_PER_CPU(struct ftrace_profile_stat, ftrace_profile_stats); #define FTRACE_PROFILE_HASH_SIZE 1024 /* must be power of 2 */ static void * function_stat_next(void *v, int idx) { struct ftrace_profile *rec = v; struct ftrace_profile_page *pg; pg = (struct ftrace_profile_page *)((unsigned long)rec & PAGE_MASK); again: if (idx != 0) rec++; if ((void *)rec >= (void *)&pg->records[pg->index]) { pg = pg->next; if (!pg) return NULL; rec = &pg->records[0]; if (!rec->counter) goto again; } return rec; } static void *function_stat_start(struct tracer_stat *trace) { struct ftrace_profile_stat *stat = container_of(trace, struct ftrace_profile_stat, stat); if (!stat || !stat->start) return NULL; return function_stat_next(&stat->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) { struct ftrace_profile *a = p1; struct ftrace_profile *b = p2; if (a->counter < b->counter) return -1; if (a->counter > b->counter) return 1; else return 0; } #endif static int function_stat_headers(struct seq_file *m) { #ifdef CONFIG_FUNCTION_GRAPH_TRACER seq_printf(m, " Function " "Hit Time Avg s^2\n" " -------- " "--- ---- --- ---\n"); #else seq_printf(m, " Function Hit\n" " -------- ---\n"); #endif return 0; } static int function_stat_show(struct seq_file *m, void *v) { struct ftrace_profile *rec = v; char str[KSYM_SYMBOL_LEN]; int ret = 0; #ifdef CONFIG_FUNCTION_GRAPH_TRACER static struct trace_seq s; unsigned long long avg; unsigned long long stddev; #endif mutex_lock(&ftrace_profile_lock); /* we raced with function_profile_reset() */ if (unlikely(rec->counter == 0)) { ret = -EBUSY; goto out; } 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, " "); avg = rec->time; do_div(avg, rec->counter); /* Sample standard deviation (s^2) */ if (rec->counter <= 1) stddev = 0; else { stddev = rec->time_squared - rec->counter * avg * avg; /* * Divide only 1000 for ns^2 -> us^2 conversion. * trace_print_graph_duration will divide 1000 again. */ do_div(stddev, (rec->counter - 1) * 1000); } trace_seq_init(&s); trace_print_graph_duration(rec->time, &s); trace_seq_puts(&s, " "); trace_print_graph_duration(avg, &s); trace_seq_puts(&s, " "); trace_print_graph_duration(stddev, &s); trace_print_seq(m, &s);