aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/fork.c4
-rw-r--r--kernel/sched.c2
-rw-r--r--kernel/trace/Kconfig19
-rw-r--r--kernel/trace/Makefile2
-rw-r--r--kernel/trace/ftrace.c26
-rw-r--r--kernel/trace/trace.c18
-rw-r--r--kernel/trace/trace.h12
-rw-r--r--kernel/trace/trace_functions_graph.c98
9 files changed, 142 insertions, 41 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index 03a45e7e87b7..703cf3b7389c 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -21,7 +21,7 @@ CFLAGS_REMOVE_cgroup-debug.o = -pg
21CFLAGS_REMOVE_sched_clock.o = -pg 21CFLAGS_REMOVE_sched_clock.o = -pg
22CFLAGS_REMOVE_sched.o = -pg 22CFLAGS_REMOVE_sched.o = -pg
23endif 23endif
24ifdef CONFIG_FUNCTION_RET_TRACER 24ifdef CONFIG_FUNCTION_GRAPH_TRACER
25CFLAGS_REMOVE_extable.o = -pg # For __kernel_text_address() 25CFLAGS_REMOVE_extable.o = -pg # For __kernel_text_address()
26CFLAGS_REMOVE_module.o = -pg # For __module_text_address() 26CFLAGS_REMOVE_module.o = -pg # For __module_text_address()
27endif 27endif
diff --git a/kernel/fork.c b/kernel/fork.c
index d6e1a3205f62..5f82a999c032 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -140,7 +140,7 @@ void free_task(struct task_struct *tsk)
140 prop_local_destroy_single(&tsk->dirties); 140 prop_local_destroy_single(&tsk->dirties);
141 free_thread_info(tsk->stack); 141 free_thread_info(tsk->stack);
142 rt_mutex_debug_task_free(tsk); 142 rt_mutex_debug_task_free(tsk);
143 ftrace_retfunc_exit_task(tsk); 143 ftrace_graph_exit_task(tsk);
144 free_task_struct(tsk); 144 free_task_struct(tsk);
145} 145}
146EXPORT_SYMBOL(free_task); 146EXPORT_SYMBOL(free_task);
@@ -1271,7 +1271,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
1271 total_forks++; 1271 total_forks++;
1272 spin_unlock(&current->sighand->siglock); 1272 spin_unlock(&current->sighand->siglock);
1273 write_unlock_irq(&tasklist_lock); 1273 write_unlock_irq(&tasklist_lock);
1274 ftrace_retfunc_init_task(p); 1274 ftrace_graph_init_task(p);
1275 proc_fork_connector(p); 1275 proc_fork_connector(p);
1276 cgroup_post_fork(p); 1276 cgroup_post_fork(p);
1277 return p; 1277 return p;
diff --git a/kernel/sched.c b/kernel/sched.c
index 388d9db044ab..52490bf6b884 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -5901,7 +5901,7 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu)
5901 * The idle tasks have their own, simple scheduling class: 5901 * The idle tasks have their own, simple scheduling class:
5902 */ 5902 */
5903 idle->sched_class = &idle_sched_class; 5903 idle->sched_class = &idle_sched_class;
5904 ftrace_retfunc_init_task(idle); 5904 ftrace_graph_init_task(idle);
5905} 5905}
5906 5906
5907/* 5907/*
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 620feadff67a..eb9b901e0777 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -12,7 +12,7 @@ config NOP_TRACER
12config HAVE_FUNCTION_TRACER 12config HAVE_FUNCTION_TRACER
13 bool 13 bool
14 14
15config HAVE_FUNCTION_RET_TRACER 15config HAVE_FUNCTION_GRAPH_TRACER
16 bool 16 bool
17 17
18config HAVE_FUNCTION_TRACE_MCOUNT_TEST 18config HAVE_FUNCTION_TRACE_MCOUNT_TEST
@@ -63,15 +63,18 @@ config FUNCTION_TRACER
63 (the bootup default), then the overhead of the instructions is very 63 (the bootup default), then the overhead of the instructions is very
64 small and not measurable even in micro-benchmarks. 64 small and not measurable even in micro-benchmarks.
65 65
66config FUNCTION_RET_TRACER 66config FUNCTION_GRAPH_TRACER
67 bool "Kernel Function return Tracer" 67 bool "Kernel Function Graph Tracer"
68 depends on HAVE_FUNCTION_RET_TRACER 68 depends on HAVE_FUNCTION_GRAPH_TRACER
69 depends on FUNCTION_TRACER 69 depends on FUNCTION_TRACER
70 help 70 help
71 Enable the kernel to trace a function at its return. 71 Enable the kernel to trace a function at both its return
72 It's first purpose is to trace the duration of functions. 72 and its entry.
73 This is done by setting the current return address on the thread 73 It's first purpose is to trace the duration of functions and
74 info structure of the current task. 74 draw a call graph for each thread with some informations like
75 the return value.
76 This is done by setting the current return address on the current
77 task structure into a stack of calls.
75 78
76config IRQSOFF_TRACER 79config IRQSOFF_TRACER
77 bool "Interrupts-off Latency Tracer" 80 bool "Interrupts-off Latency Tracer"
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index cef4bcb4e822..08c5fe6ddc09 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -29,7 +29,7 @@ obj-$(CONFIG_NOP_TRACER) += trace_nop.o
29obj-$(CONFIG_STACK_TRACER) += trace_stack.o 29obj-$(CONFIG_STACK_TRACER) += trace_stack.o
30obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o 30obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
31obj-$(CONFIG_BOOT_TRACER) += trace_boot.o 31obj-$(CONFIG_BOOT_TRACER) += trace_boot.o
32obj-$(CONFIG_FUNCTION_RET_TRACER) += trace_functions_return.o 32obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o
33obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o 33obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o
34obj-$(CONFIG_BTS_TRACER) += trace_bts.o 34obj-$(CONFIG_BTS_TRACER) += trace_bts.o
35 35
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 53042f118f23..9e19976af727 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -395,11 +395,11 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
395 unsigned long ip, fl; 395 unsigned long ip, fl;
396 unsigned long ftrace_addr; 396 unsigned long ftrace_addr;
397 397
398#ifdef CONFIG_FUNCTION_RET_TRACER 398#ifdef CONFIG_FUNCTION_GRAPH_TRACER
399 if (ftrace_tracing_type == FTRACE_TYPE_ENTER) 399 if (ftrace_tracing_type == FTRACE_TYPE_ENTER)
400 ftrace_addr = (unsigned long)ftrace_caller; 400 ftrace_addr = (unsigned long)ftrace_caller;
401 else 401 else
402 ftrace_addr = (unsigned long)ftrace_return_caller; 402 ftrace_addr = (unsigned long)ftrace_graph_caller;
403#else 403#else
404 ftrace_addr = (unsigned long)ftrace_caller; 404 ftrace_addr = (unsigned long)ftrace_caller;
405#endif 405#endif
@@ -1496,13 +1496,13 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
1496 return ret; 1496 return ret;
1497} 1497}
1498 1498
1499#ifdef CONFIG_FUNCTION_RET_TRACER 1499#ifdef CONFIG_FUNCTION_GRAPH_TRACER
1500 1500
1501static atomic_t ftrace_retfunc_active; 1501static atomic_t ftrace_retfunc_active;
1502 1502
1503/* The callback that hooks the return of a function */ 1503/* The callback that hooks the return of a function */
1504trace_function_return_t ftrace_function_return = 1504trace_function_graph_t ftrace_graph_function =
1505 (trace_function_return_t)ftrace_stub; 1505 (trace_function_graph_t)ftrace_stub;
1506 1506
1507 1507
1508/* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */ 1508/* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */
@@ -1549,7 +1549,7 @@ free:
1549} 1549}
1550 1550
1551/* Allocate a return stack for each task */ 1551/* Allocate a return stack for each task */
1552static int start_return_tracing(void) 1552static int start_graph_tracing(void)
1553{ 1553{
1554 struct ftrace_ret_stack **ret_stack_list; 1554 struct ftrace_ret_stack **ret_stack_list;
1555 int ret; 1555 int ret;
@@ -1569,7 +1569,7 @@ static int start_return_tracing(void)
1569 return ret; 1569 return ret;
1570} 1570}
1571 1571
1572int register_ftrace_return(trace_function_return_t func) 1572int register_ftrace_graph(trace_function_graph_t func)
1573{ 1573{
1574 int ret = 0; 1574 int ret = 0;
1575 1575
@@ -1584,13 +1584,13 @@ int register_ftrace_return(trace_function_return_t func)
1584 goto out; 1584 goto out;
1585 } 1585 }
1586 atomic_inc(&ftrace_retfunc_active); 1586 atomic_inc(&ftrace_retfunc_active);
1587 ret = start_return_tracing(); 1587 ret = start_graph_tracing();
1588 if (ret) { 1588 if (ret) {
1589 atomic_dec(&ftrace_retfunc_active); 1589 atomic_dec(&ftrace_retfunc_active);
1590 goto out; 1590 goto out;
1591 } 1591 }
1592 ftrace_tracing_type = FTRACE_TYPE_RETURN; 1592 ftrace_tracing_type = FTRACE_TYPE_RETURN;
1593 ftrace_function_return = func; 1593 ftrace_graph_function = func;
1594 ftrace_startup(); 1594 ftrace_startup();
1595 1595
1596out: 1596out:
@@ -1598,12 +1598,12 @@ out:
1598 return ret; 1598 return ret;
1599} 1599}
1600 1600
1601void unregister_ftrace_return(void) 1601void unregister_ftrace_graph(void)
1602{ 1602{
1603 mutex_lock(&ftrace_sysctl_lock); 1603 mutex_lock(&ftrace_sysctl_lock);
1604 1604
1605 atomic_dec(&ftrace_retfunc_active); 1605 atomic_dec(&ftrace_retfunc_active);
1606 ftrace_function_return = (trace_function_return_t)ftrace_stub; 1606 ftrace_graph_function = (trace_function_graph_t)ftrace_stub;
1607 ftrace_shutdown(); 1607 ftrace_shutdown();
1608 /* Restore normal tracing type */ 1608 /* Restore normal tracing type */
1609 ftrace_tracing_type = FTRACE_TYPE_ENTER; 1609 ftrace_tracing_type = FTRACE_TYPE_ENTER;
@@ -1612,7 +1612,7 @@ void unregister_ftrace_return(void)
1612} 1612}
1613 1613
1614/* Allocate a return stack for newly created task */ 1614/* Allocate a return stack for newly created task */
1615void ftrace_retfunc_init_task(struct task_struct *t) 1615void ftrace_graph_init_task(struct task_struct *t)
1616{ 1616{
1617 if (atomic_read(&ftrace_retfunc_active)) { 1617 if (atomic_read(&ftrace_retfunc_active)) {
1618 t->ret_stack = kmalloc(FTRACE_RETFUNC_DEPTH 1618 t->ret_stack = kmalloc(FTRACE_RETFUNC_DEPTH
@@ -1626,7 +1626,7 @@ void ftrace_retfunc_init_task(struct task_struct *t)
1626 t->ret_stack = NULL; 1626 t->ret_stack = NULL;
1627} 1627}
1628 1628
1629void ftrace_retfunc_exit_task(struct task_struct *t) 1629void ftrace_graph_exit_task(struct task_struct *t)
1630{ 1630{
1631 struct ftrace_ret_stack *ret_stack = t->ret_stack; 1631 struct ftrace_ret_stack *ret_stack = t->ret_stack;
1632 1632
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8df8fdd69c95..f21ab2c68fd4 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -878,15 +878,15 @@ trace_function(struct trace_array *tr, struct trace_array_cpu *data,
878 ring_buffer_unlock_commit(tr->buffer, event, irq_flags); 878 ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
879} 879}
880 880
881#ifdef CONFIG_FUNCTION_RET_TRACER 881#ifdef CONFIG_FUNCTION_GRAPH_TRACER
882static void __trace_function_return(struct trace_array *tr, 882static void __trace_function_graph(struct trace_array *tr,
883 struct trace_array_cpu *data, 883 struct trace_array_cpu *data,
884 struct ftrace_retfunc *trace, 884 struct ftrace_graph_ret *trace,
885 unsigned long flags, 885 unsigned long flags,
886 int pc) 886 int pc)
887{ 887{
888 struct ring_buffer_event *event; 888 struct ring_buffer_event *event;
889 struct ftrace_ret_entry *entry; 889 struct ftrace_graph_entry *entry;
890 unsigned long irq_flags; 890 unsigned long irq_flags;
891 891
892 if (unlikely(local_read(&__get_cpu_var(ftrace_cpu_disabled)))) 892 if (unlikely(local_read(&__get_cpu_var(ftrace_cpu_disabled))))
@@ -1177,8 +1177,8 @@ function_trace_call(unsigned long ip, unsigned long parent_ip)
1177 local_irq_restore(flags); 1177 local_irq_restore(flags);
1178} 1178}
1179 1179
1180#ifdef CONFIG_FUNCTION_RET_TRACER 1180#ifdef CONFIG_FUNCTION_GRAPH_TRACER
1181void trace_function_return(struct ftrace_retfunc *trace) 1181void trace_function_graph(struct ftrace_graph_ret *trace)
1182{ 1182{
1183 struct trace_array *tr = &global_trace; 1183 struct trace_array *tr = &global_trace;
1184 struct trace_array_cpu *data; 1184 struct trace_array_cpu *data;
@@ -1193,12 +1193,12 @@ void trace_function_return(struct ftrace_retfunc *trace)
1193 disabled = atomic_inc_return(&data->disabled); 1193 disabled = atomic_inc_return(&data->disabled);
1194 if (likely(disabled == 1)) { 1194 if (likely(disabled == 1)) {
1195 pc = preempt_count(); 1195 pc = preempt_count();
1196 __trace_function_return(tr, data, trace, flags, pc); 1196 __trace_function_graph(tr, data, trace, flags, pc);
1197 } 1197 }
1198 atomic_dec(&data->disabled); 1198 atomic_dec(&data->disabled);
1199 raw_local_irq_restore(flags); 1199 raw_local_irq_restore(flags);
1200} 1200}
1201#endif /* CONFIG_FUNCTION_RET_TRACER */ 1201#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
1202 1202
1203static struct ftrace_ops trace_ops __read_mostly = 1203static struct ftrace_ops trace_ops __read_mostly =
1204{ 1204{
@@ -2001,7 +2001,7 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
2001 break; 2001 break;
2002 } 2002 }
2003 case TRACE_FN_RET: { 2003 case TRACE_FN_RET: {
2004 return print_return_function(iter); 2004 return print_graph_function(iter);
2005 break; 2005 break;
2006 } 2006 }
2007 case TRACE_BRANCH: { 2007 case TRACE_BRANCH: {
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 3abd645e8af2..72b5ef868765 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -57,7 +57,7 @@ struct ftrace_entry {
57}; 57};
58 58
59/* Function return entry */ 59/* Function return entry */
60struct ftrace_ret_entry { 60struct ftrace_graph_entry {
61 struct trace_entry ent; 61 struct trace_entry ent;
62 unsigned long ip; 62 unsigned long ip;
63 unsigned long parent_ip; 63 unsigned long parent_ip;
@@ -264,7 +264,7 @@ extern void __ftrace_bad_type(void);
264 IF_ASSIGN(var, ent, struct trace_boot_call, TRACE_BOOT_CALL);\ 264 IF_ASSIGN(var, ent, struct trace_boot_call, TRACE_BOOT_CALL);\
265 IF_ASSIGN(var, ent, struct trace_boot_ret, TRACE_BOOT_RET);\ 265 IF_ASSIGN(var, ent, struct trace_boot_ret, TRACE_BOOT_RET);\
266 IF_ASSIGN(var, ent, struct trace_branch, TRACE_BRANCH); \ 266 IF_ASSIGN(var, ent, struct trace_branch, TRACE_BRANCH); \
267 IF_ASSIGN(var, ent, struct ftrace_ret_entry, TRACE_FN_RET);\ 267 IF_ASSIGN(var, ent, struct ftrace_graph_entry, TRACE_FN_RET);\
268 IF_ASSIGN(var, ent, struct bts_entry, TRACE_BTS);\ 268 IF_ASSIGN(var, ent, struct bts_entry, TRACE_BTS);\
269 __ftrace_bad_type(); \ 269 __ftrace_bad_type(); \
270 } while (0) 270 } while (0)
@@ -398,7 +398,7 @@ void trace_function(struct trace_array *tr,
398 unsigned long parent_ip, 398 unsigned long parent_ip,
399 unsigned long flags, int pc); 399 unsigned long flags, int pc);
400void 400void
401trace_function_return(struct ftrace_retfunc *trace); 401trace_function_graph(struct ftrace_graph_ret *trace);
402 402
403void trace_bts(struct trace_array *tr, 403void trace_bts(struct trace_array *tr,
404 unsigned long from, 404 unsigned long from,
@@ -489,11 +489,11 @@ extern int trace_vprintk(unsigned long ip, const char *fmt, va_list args);
489extern unsigned long trace_flags; 489extern unsigned long trace_flags;
490 490
491/* Standard output formatting function used for function return traces */ 491/* Standard output formatting function used for function return traces */
492#ifdef CONFIG_FUNCTION_RET_TRACER 492#ifdef CONFIG_FUNCTION_GRAPH_TRACER
493extern enum print_line_t print_return_function(struct trace_iterator *iter); 493extern enum print_line_t print_graph_function(struct trace_iterator *iter);
494#else 494#else
495static inline enum print_line_t 495static inline enum print_line_t
496print_return_function(struct trace_iterator *iter) 496print_graph_function(struct trace_iterator *iter)
497{ 497{
498 return TRACE_TYPE_UNHANDLED; 498 return TRACE_TYPE_UNHANDLED;
499} 499}
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
new file mode 100644
index 000000000000..f5bad4624d2b
--- /dev/null
+++ b/kernel/trace/trace_functions_graph.c
@@ -0,0 +1,98 @@
1/*
2 *
3 * Function graph tracer.
4 * Copyright (c) 2008 Frederic Weisbecker <fweisbec@gmail.com>
5 * Mostly borrowed from function tracer which
6 * is Copyright (c) Steven Rostedt <srostedt@redhat.com>
7 *
8 */
9#include <linux/debugfs.h>
10#include <linux/uaccess.h>
11#include <linux/ftrace.h>
12#include <linux/fs.h>
13
14#include "trace.h"
15
16
17#define TRACE_GRAPH_PRINT_OVERRUN 0x1
18static struct tracer_opt trace_opts[] = {
19 /* Display overruns or not */
20 { TRACER_OPT(overrun, TRACE_GRAPH_PRINT_OVERRUN) },
21 { } /* Empty entry */
22};
23
24static struct tracer_flags tracer_flags = {
25 .val = 0, /* Don't display overruns by default */
26 .opts = trace_opts
27};
28
29
30static int graph_trace_init(struct trace_array *tr)
31{
32 int cpu;
33 for_each_online_cpu(cpu)
34 tracing_reset(tr, cpu);
35
36 return register_ftrace_graph(&trace_function_graph);
37}
38
39static void graph_trace_reset(struct trace_array *tr)
40{
41 unregister_ftrace_graph();
42}
43
44
45enum print_line_t
46print_graph_function(struct trace_iterator *iter)
47{
48 struct trace_seq *s = &iter->seq;
49 struct trace_entry *entry = iter->ent;
50 struct ftrace_graph_entry *field;
51 int ret;
52
53 if (entry->type == TRACE_FN_RET) {
54 trace_assign_type(field, entry);
55 ret = trace_seq_printf(s, "%pF -> ", (void *)field->parent_ip);
56 if (!ret)
57 return TRACE_TYPE_PARTIAL_LINE;
58
59 ret = seq_print_ip_sym(s, field->ip,
60 trace_flags & TRACE_ITER_SYM_MASK);
61 if (!ret)
62 return TRACE_TYPE_PARTIAL_LINE;
63
64 ret = trace_seq_printf(s, " (%llu ns)",
65 field->rettime - field->calltime);
66 if (!ret)
67 return TRACE_TYPE_PARTIAL_LINE;
68
69 if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) {
70 ret = trace_seq_printf(s, " (Overruns: %lu)",
71 field->overrun);
72 if (!ret)
73 return TRACE_TYPE_PARTIAL_LINE;
74 }
75
76 ret = trace_seq_printf(s, "\n");
77 if (!ret)
78 return TRACE_TYPE_PARTIAL_LINE;
79
80 return TRACE_TYPE_HANDLED;
81 }
82 return TRACE_TYPE_UNHANDLED;
83}
84
85static struct tracer graph_trace __read_mostly = {
86 .name = "function-graph",
87 .init = graph_trace_init,
88 .reset = graph_trace_reset,
89 .print_line = print_graph_function,
90 .flags = &tracer_flags,
91};
92
93static __init int init_graph_trace(void)
94{
95 return register_tracer(&graph_trace);
96}
97
98device_initcall(init_graph_trace);