diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
| -rw-r--r-- | kernel/trace/ftrace.c | 41 |
1 files changed, 31 insertions, 10 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 2404b59b3097..0d88ce9b9fb8 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -264,6 +264,7 @@ struct ftrace_profile { | |||
| 264 | unsigned long counter; | 264 | unsigned long counter; |
| 265 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 265 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 266 | unsigned long long time; | 266 | unsigned long long time; |
| 267 | unsigned long long time_squared; | ||
| 267 | #endif | 268 | #endif |
| 268 | }; | 269 | }; |
| 269 | 270 | ||
| @@ -366,9 +367,9 @@ static int function_stat_headers(struct seq_file *m) | |||
| 366 | { | 367 | { |
| 367 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 368 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 368 | seq_printf(m, " Function " | 369 | seq_printf(m, " Function " |
| 369 | "Hit Time Avg\n" | 370 | "Hit Time Avg s^2\n" |
| 370 | " -------- " | 371 | " -------- " |
| 371 | "--- ---- ---\n"); | 372 | "--- ---- --- ---\n"); |
| 372 | #else | 373 | #else |
| 373 | seq_printf(m, " Function Hit\n" | 374 | seq_printf(m, " Function Hit\n" |
| 374 | " -------- ---\n"); | 375 | " -------- ---\n"); |
| @@ -384,6 +385,7 @@ static int function_stat_show(struct seq_file *m, void *v) | |||
| 384 | static DEFINE_MUTEX(mutex); | 385 | static DEFINE_MUTEX(mutex); |
| 385 | static struct trace_seq s; | 386 | static struct trace_seq s; |
| 386 | unsigned long long avg; | 387 | unsigned long long avg; |
| 388 | unsigned long long stddev; | ||
| 387 | #endif | 389 | #endif |
| 388 | 390 | ||
| 389 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | 391 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); |
| @@ -394,11 +396,25 @@ static int function_stat_show(struct seq_file *m, void *v) | |||
| 394 | avg = rec->time; | 396 | avg = rec->time; |
| 395 | do_div(avg, rec->counter); | 397 | do_div(avg, rec->counter); |
| 396 | 398 | ||
| 399 | /* Sample standard deviation (s^2) */ | ||
| 400 | if (rec->counter <= 1) | ||
| 401 | stddev = 0; | ||
| 402 | else { | ||
| 403 | stddev = rec->time_squared - rec->counter * avg * avg; | ||
| 404 | /* | ||
| 405 | * Divide only 1000 for ns^2 -> us^2 conversion. | ||
| 406 | * trace_print_graph_duration will divide 1000 again. | ||
| 407 | */ | ||
| 408 | do_div(stddev, (rec->counter - 1) * 1000); | ||
| 409 | } | ||
| 410 | |||
| 397 | mutex_lock(&mutex); | 411 | mutex_lock(&mutex); |
| 398 | trace_seq_init(&s); | 412 | trace_seq_init(&s); |
| 399 | trace_print_graph_duration(rec->time, &s); | 413 | trace_print_graph_duration(rec->time, &s); |
| 400 | trace_seq_puts(&s, " "); | 414 | trace_seq_puts(&s, " "); |
| 401 | trace_print_graph_duration(avg, &s); | 415 | trace_print_graph_duration(avg, &s); |
| 416 | trace_seq_puts(&s, " "); | ||
| 417 | trace_print_graph_duration(stddev, &s); | ||
| 402 | trace_print_seq(m, &s); | 418 | trace_print_seq(m, &s); |
| 403 | mutex_unlock(&mutex); | 419 | mutex_unlock(&mutex); |
| 404 | #endif | 420 | #endif |
| @@ -650,6 +666,10 @@ static void profile_graph_return(struct ftrace_graph_ret *trace) | |||
| 650 | if (!stat->hash || !ftrace_profile_enabled) | 666 | if (!stat->hash || !ftrace_profile_enabled) |
| 651 | goto out; | 667 | goto out; |
| 652 | 668 | ||
| 669 | /* If the calltime was zero'd ignore it */ | ||
| 670 | if (!trace->calltime) | ||
| 671 | goto out; | ||
| 672 | |||
| 653 | calltime = trace->rettime - trace->calltime; | 673 | calltime = trace->rettime - trace->calltime; |
| 654 | 674 | ||
| 655 | if (!(trace_flags & TRACE_ITER_GRAPH_TIME)) { | 675 | if (!(trace_flags & TRACE_ITER_GRAPH_TIME)) { |
| @@ -668,8 +688,10 @@ static void profile_graph_return(struct ftrace_graph_ret *trace) | |||
| 668 | } | 688 | } |
| 669 | 689 | ||
| 670 | rec = ftrace_find_profiled_func(stat, trace->func); | 690 | rec = ftrace_find_profiled_func(stat, trace->func); |
| 671 | if (rec) | 691 | if (rec) { |
| 672 | rec->time += calltime; | 692 | rec->time += calltime; |
| 693 | rec->time_squared += calltime * calltime; | ||
| 694 | } | ||
| 673 | 695 | ||
| 674 | out: | 696 | out: |
| 675 | local_irq_restore(flags); | 697 | local_irq_restore(flags); |
| @@ -1861,7 +1883,6 @@ function_trace_probe_call(unsigned long ip, unsigned long parent_ip) | |||
| 1861 | struct hlist_head *hhd; | 1883 | struct hlist_head *hhd; |
| 1862 | struct hlist_node *n; | 1884 | struct hlist_node *n; |
| 1863 | unsigned long key; | 1885 | unsigned long key; |
| 1864 | int resched; | ||
| 1865 | 1886 | ||
| 1866 | key = hash_long(ip, FTRACE_HASH_BITS); | 1887 | key = hash_long(ip, FTRACE_HASH_BITS); |
| 1867 | 1888 | ||
| @@ -1875,12 +1896,12 @@ function_trace_probe_call(unsigned long ip, unsigned long parent_ip) | |||
| 1875 | * period. This syncs the hash iteration and freeing of items | 1896 | * period. This syncs the hash iteration and freeing of items |
| 1876 | * on the hash. rcu_read_lock is too dangerous here. | 1897 | * on the hash. rcu_read_lock is too dangerous here. |
| 1877 | */ | 1898 | */ |
| 1878 | resched = ftrace_preempt_disable(); | 1899 | preempt_disable_notrace(); |
| 1879 | hlist_for_each_entry_rcu(entry, n, hhd, node) { | 1900 | hlist_for_each_entry_rcu(entry, n, hhd, node) { |
| 1880 | if (entry->ip == ip) | 1901 | if (entry->ip == ip) |
| 1881 | entry->ops->func(ip, parent_ip, &entry->data); | 1902 | entry->ops->func(ip, parent_ip, &entry->data); |
| 1882 | } | 1903 | } |
| 1883 | ftrace_preempt_enable(resched); | 1904 | preempt_enable_notrace(); |
| 1884 | } | 1905 | } |
| 1885 | 1906 | ||
| 1886 | static struct ftrace_ops trace_probe_ops __read_mostly = | 1907 | static struct ftrace_ops trace_probe_ops __read_mostly = |
| @@ -3212,8 +3233,8 @@ free: | |||
| 3212 | } | 3233 | } |
| 3213 | 3234 | ||
| 3214 | static void | 3235 | static void |
| 3215 | ftrace_graph_probe_sched_switch(struct rq *__rq, struct task_struct *prev, | 3236 | ftrace_graph_probe_sched_switch(void *ignore, |
| 3216 | struct task_struct *next) | 3237 | struct task_struct *prev, struct task_struct *next) |
| 3217 | { | 3238 | { |
| 3218 | unsigned long long timestamp; | 3239 | unsigned long long timestamp; |
| 3219 | int index; | 3240 | int index; |
| @@ -3267,7 +3288,7 @@ static int start_graph_tracing(void) | |||
| 3267 | } while (ret == -EAGAIN); | 3288 | } while (ret == -EAGAIN); |
| 3268 | 3289 | ||
| 3269 | if (!ret) { | 3290 | if (!ret) { |
| 3270 | ret = register_trace_sched_switch(ftrace_graph_probe_sched_switch); | 3291 | ret = register_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL); |
| 3271 | if (ret) | 3292 | if (ret) |
| 3272 | pr_info("ftrace_graph: Couldn't activate tracepoint" | 3293 | pr_info("ftrace_graph: Couldn't activate tracepoint" |
| 3273 | " probe to kernel_sched_switch\n"); | 3294 | " probe to kernel_sched_switch\n"); |
| @@ -3339,11 +3360,11 @@ void unregister_ftrace_graph(void) | |||
| 3339 | goto out; | 3360 | goto out; |
| 3340 | 3361 | ||
| 3341 | ftrace_graph_active--; | 3362 | ftrace_graph_active--; |
| 3342 | unregister_trace_sched_switch(ftrace_graph_probe_sched_switch); | ||
| 3343 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; | 3363 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; |
| 3344 | ftrace_graph_entry = ftrace_graph_entry_stub; | 3364 | ftrace_graph_entry = ftrace_graph_entry_stub; |
| 3345 | ftrace_shutdown(FTRACE_STOP_FUNC_RET); | 3365 | ftrace_shutdown(FTRACE_STOP_FUNC_RET); |
| 3346 | unregister_pm_notifier(&ftrace_suspend_notifier); | 3366 | unregister_pm_notifier(&ftrace_suspend_notifier); |
| 3367 | unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL); | ||
| 3347 | 3368 | ||
| 3348 | out: | 3369 | out: |
| 3349 | mutex_unlock(&ftrace_lock); | 3370 | mutex_unlock(&ftrace_lock); |
