aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/trace/Kconfig14
-rw-r--r--kernel/trace/Makefile1
-rw-r--r--kernel/trace/ftrace.c16
-rw-r--r--kernel/trace/trace.c65
-rw-r--r--kernel/trace/trace.h35
-rw-r--r--kernel/trace/trace_functions_return.c82
6 files changed, 205 insertions, 8 deletions
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index fc4febc3334a..d986216c8327 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -9,6 +9,9 @@ config NOP_TRACER
9config HAVE_FUNCTION_TRACER 9config HAVE_FUNCTION_TRACER
10 bool 10 bool
11 11
12config HAVE_FUNCTION_RET_TRACER
13 bool
14
12config HAVE_FUNCTION_TRACE_MCOUNT_TEST 15config HAVE_FUNCTION_TRACE_MCOUNT_TEST
13 bool 16 bool
14 help 17 help
@@ -54,6 +57,17 @@ config FUNCTION_TRACER
54 (the bootup default), then the overhead of the instructions is very 57 (the bootup default), then the overhead of the instructions is very
55 small and not measurable even in micro-benchmarks. 58 small and not measurable even in micro-benchmarks.
56 59
60config FUNCTION_RET_TRACER
61 bool "Kernel Function return Tracer"
62 depends on !DYNAMIC_FTRACE
63 depends on HAVE_FUNCTION_RET_TRACER
64 depends on FUNCTION_TRACER
65 help
66 Enable the kernel to trace a function at its return.
67 It's first purpose is to trace the duration of functions.
68 This is done by setting the current return address on the thread
69 info structure of the current task.
70
57config IRQSOFF_TRACER 71config IRQSOFF_TRACER
58 bool "Interrupts-off Latency Tracer" 72 bool "Interrupts-off Latency Tracer"
59 default n 73 default n
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index c8228b1a49e9..3e1f361bbc17 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -24,5 +24,6 @@ obj-$(CONFIG_NOP_TRACER) += trace_nop.o
24obj-$(CONFIG_STACK_TRACER) += trace_stack.o 24obj-$(CONFIG_STACK_TRACER) += trace_stack.o
25obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o 25obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
26obj-$(CONFIG_BOOT_TRACER) += trace_boot.o 26obj-$(CONFIG_BOOT_TRACER) += trace_boot.o
27obj-$(CONFIG_FUNCTION_RET_TRACER) += trace_functions_return.o
27 28
28libftrace-y := ftrace.o 29libftrace-y := ftrace.o
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 4d2e751bfb11..f03fe74ecd67 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1484,3 +1484,19 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
1484 return ret; 1484 return ret;
1485} 1485}
1486 1486
1487#ifdef CONFIG_FUNCTION_RET_TRACER
1488trace_function_return_t ftrace_function_return =
1489 (trace_function_return_t)ftrace_stub;
1490void register_ftrace_return(trace_function_return_t func)
1491{
1492 ftrace_function_return = func;
1493}
1494
1495void unregister_ftrace_return(void)
1496{
1497 ftrace_function_return = (trace_function_return_t)ftrace_stub;
1498}
1499#endif
1500
1501
1502
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 216bbe7547a4..a3f7ae9cd8e1 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -244,13 +244,6 @@ unsigned long nsecs_to_usecs(unsigned long nsecs)
244 return nsecs / 1000; 244 return nsecs / 1000;
245} 245}
246 246
247/*
248 * TRACE_ITER_SYM_MASK masks the options in trace_flags that
249 * control the output of kernel symbols.
250 */
251#define TRACE_ITER_SYM_MASK \
252 (TRACE_ITER_PRINT_PARENT|TRACE_ITER_SYM_OFFSET|TRACE_ITER_SYM_ADDR)
253
254/* These must match the bit postions in trace_iterator_flags */ 247/* These must match the bit postions in trace_iterator_flags */
255static const char *trace_options[] = { 248static const char *trace_options[] = {
256 "print-parent", 249 "print-parent",
@@ -810,6 +803,35 @@ trace_function(struct trace_array *tr, struct trace_array_cpu *data,
810 ring_buffer_unlock_commit(tr->buffer, event, irq_flags); 803 ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
811} 804}
812 805
806#ifdef CONFIG_FUNCTION_RET_TRACER
807static void __trace_function_return(struct trace_array *tr,
808 struct trace_array_cpu *data,
809 struct ftrace_retfunc *trace,
810 unsigned long flags,
811 int pc)
812{
813 struct ring_buffer_event *event;
814 struct ftrace_ret_entry *entry;
815 unsigned long irq_flags;
816
817 if (unlikely(local_read(&__get_cpu_var(ftrace_cpu_disabled))))
818 return;
819
820 event = ring_buffer_lock_reserve(global_trace.buffer, sizeof(*entry),
821 &irq_flags);
822 if (!event)
823 return;
824 entry = ring_buffer_event_data(event);
825 tracing_generic_entry_update(&entry->ent, flags, pc);
826 entry->ent.type = TRACE_FN_RET;
827 entry->ip = trace->func;
828 entry->parent_ip = trace->ret;
829 entry->rettime = trace->rettime;
830 entry->calltime = trace->calltime;
831 ring_buffer_unlock_commit(global_trace.buffer, event, irq_flags);
832}
833#endif
834
813void 835void
814ftrace(struct trace_array *tr, struct trace_array_cpu *data, 836ftrace(struct trace_array *tr, struct trace_array_cpu *data,
815 unsigned long ip, unsigned long parent_ip, unsigned long flags, 837 unsigned long ip, unsigned long parent_ip, unsigned long flags,
@@ -1038,6 +1060,29 @@ function_trace_call(unsigned long ip, unsigned long parent_ip)
1038 raw_local_irq_restore(flags); 1060 raw_local_irq_restore(flags);
1039} 1061}
1040 1062
1063#ifdef CONFIG_FUNCTION_RET_TRACER
1064void trace_function_return(struct ftrace_retfunc *trace)
1065{
1066 struct trace_array *tr = &global_trace;
1067 struct trace_array_cpu *data;
1068 unsigned long flags;
1069 long disabled;
1070 int cpu;
1071 int pc;
1072
1073 raw_local_irq_save(flags);
1074 cpu = raw_smp_processor_id();
1075 data = tr->data[cpu];
1076 disabled = atomic_inc_return(&data->disabled);
1077 if (likely(disabled == 1)) {
1078 pc = preempt_count();
1079 __trace_function_return(tr, data, trace, flags, pc);
1080 }
1081 atomic_dec(&data->disabled);
1082 raw_local_irq_restore(flags);
1083}
1084#endif /* CONFIG_FUNCTION_RET_TRACER */
1085
1041static struct ftrace_ops trace_ops __read_mostly = 1086static struct ftrace_ops trace_ops __read_mostly =
1042{ 1087{
1043 .func = function_trace_call, 1088 .func = function_trace_call,
@@ -1285,7 +1330,7 @@ seq_print_sym_offset(struct trace_seq *s, const char *fmt,
1285# define IP_FMT "%016lx" 1330# define IP_FMT "%016lx"
1286#endif 1331#endif
1287 1332
1288static int 1333int
1289seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags) 1334seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)
1290{ 1335{
1291 int ret; 1336 int ret;
@@ -1738,6 +1783,10 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
1738 trace_seq_print_cont(s, iter); 1783 trace_seq_print_cont(s, iter);
1739 break; 1784 break;
1740 } 1785 }
1786 case TRACE_FN_RET: {
1787 return print_return_function(iter);
1788 break;
1789 }
1741 } 1790 }
1742 return TRACE_TYPE_HANDLED; 1791 return TRACE_TYPE_HANDLED;
1743} 1792}
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 978145088fb8..e40ce0c14690 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -22,6 +22,7 @@ enum trace_type {
22 TRACE_MMIO_RW, 22 TRACE_MMIO_RW,
23 TRACE_MMIO_MAP, 23 TRACE_MMIO_MAP,
24 TRACE_BOOT, 24 TRACE_BOOT,
25 TRACE_FN_RET,
25 26
26 __TRACE_LAST_TYPE 27 __TRACE_LAST_TYPE
27}; 28};
@@ -48,6 +49,15 @@ struct ftrace_entry {
48 unsigned long ip; 49 unsigned long ip;
49 unsigned long parent_ip; 50 unsigned long parent_ip;
50}; 51};
52
53/* Function return entry */
54struct ftrace_ret_entry {
55 struct trace_entry ent;
56 unsigned long ip;
57 unsigned long parent_ip;
58 unsigned long long calltime;
59 unsigned long long rettime;
60};
51extern struct tracer boot_tracer; 61extern struct tracer boot_tracer;
52 62
53/* 63/*
@@ -218,6 +228,7 @@ extern void __ftrace_bad_type(void);
218 IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \ 228 IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \
219 TRACE_MMIO_MAP); \ 229 TRACE_MMIO_MAP); \
220 IF_ASSIGN(var, ent, struct trace_boot, TRACE_BOOT); \ 230 IF_ASSIGN(var, ent, struct trace_boot, TRACE_BOOT); \
231 IF_ASSIGN(var, ent, struct ftrace_ret_entry, TRACE_FN_RET); \
221 __ftrace_bad_type(); \ 232 __ftrace_bad_type(); \
222 } while (0) 233 } while (0)
223 234
@@ -321,6 +332,8 @@ void trace_function(struct trace_array *tr,
321 unsigned long ip, 332 unsigned long ip,
322 unsigned long parent_ip, 333 unsigned long parent_ip,
323 unsigned long flags, int pc); 334 unsigned long flags, int pc);
335void
336trace_function_return(struct ftrace_retfunc *trace);
324 337
325void tracing_start_cmdline_record(void); 338void tracing_start_cmdline_record(void);
326void tracing_stop_cmdline_record(void); 339void tracing_stop_cmdline_record(void);
@@ -393,6 +406,10 @@ extern void *head_page(struct trace_array_cpu *data);
393extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...); 406extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...);
394extern void trace_seq_print_cont(struct trace_seq *s, 407extern void trace_seq_print_cont(struct trace_seq *s,
395 struct trace_iterator *iter); 408 struct trace_iterator *iter);
409
410extern int
411seq_print_ip_sym(struct trace_seq *s, unsigned long ip,
412 unsigned long sym_flags);
396extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, 413extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf,
397 size_t cnt); 414 size_t cnt);
398extern long ns2usecs(cycle_t nsec); 415extern long ns2usecs(cycle_t nsec);
@@ -400,6 +417,17 @@ extern int trace_vprintk(unsigned long ip, const char *fmt, va_list args);
400 417
401extern unsigned long trace_flags; 418extern unsigned long trace_flags;
402 419
420/* Standard output formatting function used for function return traces */
421#ifdef CONFIG_FUNCTION_RET_TRACER
422extern enum print_line_t print_return_function(struct trace_iterator *iter);
423#else
424static inline enum print_line_t
425print_return_function(struct trace_iterator *iter)
426{
427 return TRACE_TYPE_UNHANDLED;
428}
429#endif
430
403/* 431/*
404 * trace_iterator_flags is an enumeration that defines bit 432 * trace_iterator_flags is an enumeration that defines bit
405 * positions into trace_flags that controls the output. 433 * positions into trace_flags that controls the output.
@@ -422,6 +450,13 @@ enum trace_iterator_flags {
422 TRACE_ITER_PREEMPTONLY = 0x800, 450 TRACE_ITER_PREEMPTONLY = 0x800,
423}; 451};
424 452
453/*
454 * TRACE_ITER_SYM_MASK masks the options in trace_flags that
455 * control the output of kernel symbols.
456 */
457#define TRACE_ITER_SYM_MASK \
458 (TRACE_ITER_PRINT_PARENT|TRACE_ITER_SYM_OFFSET|TRACE_ITER_SYM_ADDR)
459
425extern struct tracer nop_trace; 460extern struct tracer nop_trace;
426 461
427/** 462/**
diff --git a/kernel/trace/trace_functions_return.c b/kernel/trace/trace_functions_return.c
new file mode 100644
index 000000000000..7680b21537dd
--- /dev/null
+++ b/kernel/trace/trace_functions_return.c
@@ -0,0 +1,82 @@
1/*
2 *
3 * Function return 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
17static void start_return_trace(struct trace_array *tr)
18{
19 register_ftrace_return(&trace_function_return);
20}
21
22static void stop_return_trace(struct trace_array *tr)
23{
24 unregister_ftrace_return();
25}
26
27static void return_trace_init(struct trace_array *tr)
28{
29 int cpu;
30 for_each_online_cpu(cpu)
31 tracing_reset(tr, cpu);
32
33 start_return_trace(tr);
34}
35
36static void return_trace_reset(struct trace_array *tr)
37{
38 stop_return_trace(tr);
39}
40
41
42enum print_line_t
43print_return_function(struct trace_iterator *iter)
44{
45 struct trace_seq *s = &iter->seq;
46 struct trace_entry *entry = iter->ent;
47 struct ftrace_ret_entry *field;
48 int ret;
49
50 if (entry->type == TRACE_FN_RET) {
51 trace_assign_type(field, entry);
52 ret = trace_seq_printf(s, "%pF -> ", (void *)field->parent_ip);
53 if (!ret)
54 return TRACE_TYPE_PARTIAL_LINE;
55 ret = seq_print_ip_sym(s, field->ip,
56 trace_flags & TRACE_ITER_SYM_MASK);
57 if (!ret)
58 return TRACE_TYPE_PARTIAL_LINE;
59 ret = trace_seq_printf(s, " (%llu ns)\n",
60 field->rettime - field->calltime);
61 if (!ret)
62 return TRACE_TYPE_PARTIAL_LINE;
63 else
64 return TRACE_TYPE_HANDLED;
65 }
66 return TRACE_TYPE_UNHANDLED;
67}
68
69static struct tracer return_trace __read_mostly =
70{
71 .name = "return",
72 .init = return_trace_init,
73 .reset = return_trace_reset,
74 .print_line = print_return_function
75};
76
77static __init int init_return_trace(void)
78{
79 return register_tracer(&return_trace);
80}
81
82device_initcall(init_return_trace);