diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | arch/x86/Kconfig | 1 | ||||
-rw-r--r-- | arch/x86/kernel/entry_32.S | 27 | ||||
-rw-r--r-- | arch/x86/kernel/entry_64.S | 37 | ||||
-rw-r--r-- | include/linux/ftrace.h | 38 | ||||
-rw-r--r-- | kernel/Makefile | 1 | ||||
-rw-r--r-- | kernel/trace/Kconfig | 5 | ||||
-rw-r--r-- | kernel/trace/Makefile | 3 | ||||
-rw-r--r-- | kernel/trace/ftrace.c | 138 | ||||
-rw-r--r-- | lib/Kconfig.debug | 2 |
10 files changed, 256 insertions, 0 deletions
@@ -528,6 +528,10 @@ KBUILD_CFLAGS += -g | |||
528 | KBUILD_AFLAGS += -gdwarf-2 | 528 | KBUILD_AFLAGS += -gdwarf-2 |
529 | endif | 529 | endif |
530 | 530 | ||
531 | ifdef CONFIG_FTRACE | ||
532 | KBUILD_CFLAGS += -pg | ||
533 | endif | ||
534 | |||
531 | # We trigger additional mismatches with less inlining | 535 | # We trigger additional mismatches with less inlining |
532 | ifdef CONFIG_DEBUG_SECTION_MISMATCH | 536 | ifdef CONFIG_DEBUG_SECTION_MISMATCH |
533 | KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once) | 537 | KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once) |
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index fe361ae7ef2f..c742dfeb0dbe 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -23,6 +23,7 @@ config X86 | |||
23 | select HAVE_OPROFILE | 23 | select HAVE_OPROFILE |
24 | select HAVE_KPROBES | 24 | select HAVE_KPROBES |
25 | select HAVE_KRETPROBES | 25 | select HAVE_KRETPROBES |
26 | select HAVE_FTRACE | ||
26 | select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64) | 27 | select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64) |
27 | select HAVE_ARCH_KGDB if !X86_VOYAGER | 28 | select HAVE_ARCH_KGDB if !X86_VOYAGER |
28 | 29 | ||
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 2a609dc3271c..f47b9b5440d2 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S | |||
@@ -1109,6 +1109,33 @@ ENDPROC(xen_failsafe_callback) | |||
1109 | 1109 | ||
1110 | #endif /* CONFIG_XEN */ | 1110 | #endif /* CONFIG_XEN */ |
1111 | 1111 | ||
1112 | #ifdef CONFIG_FTRACE | ||
1113 | ENTRY(mcount) | ||
1114 | cmpl $ftrace_stub, ftrace_trace_function | ||
1115 | jnz trace | ||
1116 | |||
1117 | .globl ftrace_stub | ||
1118 | ftrace_stub: | ||
1119 | ret | ||
1120 | |||
1121 | /* taken from glibc */ | ||
1122 | trace: | ||
1123 | pushl %eax | ||
1124 | pushl %ecx | ||
1125 | pushl %edx | ||
1126 | movl 0xc(%esp), %eax | ||
1127 | movl 0x4(%ebp), %edx | ||
1128 | |||
1129 | call *ftrace_trace_function | ||
1130 | |||
1131 | popl %edx | ||
1132 | popl %ecx | ||
1133 | popl %eax | ||
1134 | |||
1135 | jmp ftrace_stub | ||
1136 | END(mcount) | ||
1137 | #endif | ||
1138 | |||
1112 | .section .rodata,"a" | 1139 | .section .rodata,"a" |
1113 | #include "syscall_table_32.S" | 1140 | #include "syscall_table_32.S" |
1114 | 1141 | ||
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 556a8df522a7..f046e0c64883 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
@@ -54,6 +54,43 @@ | |||
54 | 54 | ||
55 | .code64 | 55 | .code64 |
56 | 56 | ||
57 | #ifdef CONFIG_FTRACE | ||
58 | ENTRY(mcount) | ||
59 | cmpq $ftrace_stub, ftrace_trace_function | ||
60 | jnz trace | ||
61 | .globl ftrace_stub | ||
62 | ftrace_stub: | ||
63 | retq | ||
64 | |||
65 | trace: | ||
66 | /* taken from glibc */ | ||
67 | subq $0x38, %rsp | ||
68 | movq %rax, (%rsp) | ||
69 | movq %rcx, 8(%rsp) | ||
70 | movq %rdx, 16(%rsp) | ||
71 | movq %rsi, 24(%rsp) | ||
72 | movq %rdi, 32(%rsp) | ||
73 | movq %r8, 40(%rsp) | ||
74 | movq %r9, 48(%rsp) | ||
75 | |||
76 | movq 0x38(%rsp), %rdi | ||
77 | movq 8(%rbp), %rsi | ||
78 | |||
79 | call *ftrace_trace_function | ||
80 | |||
81 | movq 48(%rsp), %r9 | ||
82 | movq 40(%rsp), %r8 | ||
83 | movq 32(%rsp), %rdi | ||
84 | movq 24(%rsp), %rsi | ||
85 | movq 16(%rsp), %rdx | ||
86 | movq 8(%rsp), %rcx | ||
87 | movq (%rsp), %rax | ||
88 | addq $0x38, %rsp | ||
89 | |||
90 | jmp ftrace_stub | ||
91 | END(mcount) | ||
92 | #endif | ||
93 | |||
57 | #ifndef CONFIG_PREEMPT | 94 | #ifndef CONFIG_PREEMPT |
58 | #define retint_kernel retint_restore_args | 95 | #define retint_kernel retint_restore_args |
59 | #endif | 96 | #endif |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h new file mode 100644 index 000000000000..b96ef14c249a --- /dev/null +++ b/include/linux/ftrace.h | |||
@@ -0,0 +1,38 @@ | |||
1 | #ifndef _LINUX_FTRACE_H | ||
2 | #define _LINUX_FTRACE_H | ||
3 | |||
4 | #ifdef CONFIG_FTRACE | ||
5 | |||
6 | #include <linux/linkage.h> | ||
7 | |||
8 | #define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) | ||
9 | #define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) | ||
10 | #define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) | ||
11 | |||
12 | typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip); | ||
13 | |||
14 | struct ftrace_ops { | ||
15 | ftrace_func_t func; | ||
16 | struct ftrace_ops *next; | ||
17 | }; | ||
18 | |||
19 | /* | ||
20 | * The ftrace_ops must be a static and should also | ||
21 | * be read_mostly. These functions do modify read_mostly variables | ||
22 | * so use them sparely. Never free an ftrace_op or modify the | ||
23 | * next pointer after it has been registered. Even after unregistering | ||
24 | * it, the next pointer may still be used internally. | ||
25 | */ | ||
26 | int register_ftrace_function(struct ftrace_ops *ops); | ||
27 | int unregister_ftrace_function(struct ftrace_ops *ops); | ||
28 | void clear_ftrace_function(void); | ||
29 | |||
30 | extern void ftrace_stub(unsigned long a0, unsigned long a1); | ||
31 | extern void mcount(void); | ||
32 | |||
33 | #else /* !CONFIG_FTRACE */ | ||
34 | # define register_ftrace_function(ops) do { } while (0) | ||
35 | # define unregister_ftrace_function(ops) do { } while (0) | ||
36 | # define clear_ftrace_function(ops) do { } while (0) | ||
37 | #endif /* CONFIG_FTRACE */ | ||
38 | #endif /* _LINUX_FTRACE_H */ | ||
diff --git a/kernel/Makefile b/kernel/Makefile index 1c9938addb9d..fa05f6d8bdbf 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
@@ -69,6 +69,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o | |||
69 | obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o | 69 | obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o |
70 | obj-$(CONFIG_MARKERS) += marker.o | 70 | obj-$(CONFIG_MARKERS) += marker.o |
71 | obj-$(CONFIG_LATENCYTOP) += latencytop.o | 71 | obj-$(CONFIG_LATENCYTOP) += latencytop.o |
72 | obj-$(CONFIG_FTRACE) += trace/ | ||
72 | 73 | ||
73 | ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) | 74 | ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) |
74 | # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is | 75 | # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is |
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig new file mode 100644 index 000000000000..8185c91417bc --- /dev/null +++ b/kernel/trace/Kconfig | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Architectures that offer an FTRACE implementation should select HAVE_FTRACE: | ||
3 | # | ||
4 | config HAVE_FTRACE | ||
5 | bool | ||
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile new file mode 100644 index 000000000000..bf4fd215a6a9 --- /dev/null +++ b/kernel/trace/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | obj-$(CONFIG_FTRACE) += libftrace.o | ||
2 | |||
3 | libftrace-y := ftrace.o | ||
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c new file mode 100644 index 000000000000..b6a80b98a3fb --- /dev/null +++ b/kernel/trace/ftrace.c | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * Infrastructure for profiling code inserted by 'gcc -pg'. | ||
3 | * | ||
4 | * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com> | ||
5 | * Copyright (C) 2004-2008 Ingo Molnar <mingo@redhat.com> | ||
6 | * | ||
7 | * Originally ported from the -rt patch by: | ||
8 | * Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com> | ||
9 | * | ||
10 | * Based on code in the latency_tracer, that is: | ||
11 | * | ||
12 | * Copyright (C) 2004-2006 Ingo Molnar | ||
13 | * Copyright (C) 2004 William Lee Irwin III | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/ftrace.h> | ||
18 | |||
19 | static DEFINE_SPINLOCK(ftrace_func_lock); | ||
20 | static struct ftrace_ops ftrace_list_end __read_mostly = | ||
21 | { | ||
22 | .func = ftrace_stub, | ||
23 | }; | ||
24 | |||
25 | static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end; | ||
26 | ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; | ||
27 | |||
28 | /* mcount is defined per arch in assembly */ | ||
29 | EXPORT_SYMBOL(mcount); | ||
30 | |||
31 | notrace void ftrace_list_func(unsigned long ip, unsigned long parent_ip) | ||
32 | { | ||
33 | struct ftrace_ops *op = ftrace_list; | ||
34 | |||
35 | /* in case someone actually ports this to alpha! */ | ||
36 | read_barrier_depends(); | ||
37 | |||
38 | while (op != &ftrace_list_end) { | ||
39 | /* silly alpha */ | ||
40 | read_barrier_depends(); | ||
41 | op->func(ip, parent_ip); | ||
42 | op = op->next; | ||
43 | }; | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * register_ftrace_function - register a function for profiling | ||
48 | * @ops - ops structure that holds the function for profiling. | ||
49 | * | ||
50 | * Register a function to be called by all functions in the | ||
51 | * kernel. | ||
52 | * | ||
53 | * Note: @ops->func and all the functions it calls must be labeled | ||
54 | * with "notrace", otherwise it will go into a | ||
55 | * recursive loop. | ||
56 | */ | ||
57 | int register_ftrace_function(struct ftrace_ops *ops) | ||
58 | { | ||
59 | unsigned long flags; | ||
60 | |||
61 | spin_lock_irqsave(&ftrace_func_lock, flags); | ||
62 | ops->next = ftrace_list; | ||
63 | /* | ||
64 | * We are entering ops into the ftrace_list but another | ||
65 | * CPU might be walking that list. We need to make sure | ||
66 | * the ops->next pointer is valid before another CPU sees | ||
67 | * the ops pointer included into the ftrace_list. | ||
68 | */ | ||
69 | smp_wmb(); | ||
70 | ftrace_list = ops; | ||
71 | /* | ||
72 | * For one func, simply call it directly. | ||
73 | * For more than one func, call the chain. | ||
74 | */ | ||
75 | if (ops->next == &ftrace_list_end) | ||
76 | ftrace_trace_function = ops->func; | ||
77 | else | ||
78 | ftrace_trace_function = ftrace_list_func; | ||
79 | spin_unlock_irqrestore(&ftrace_func_lock, flags); | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | /** | ||
85 | * unregister_ftrace_function - unresgister a function for profiling. | ||
86 | * @ops - ops structure that holds the function to unregister | ||
87 | * | ||
88 | * Unregister a function that was added to be called by ftrace profiling. | ||
89 | */ | ||
90 | int unregister_ftrace_function(struct ftrace_ops *ops) | ||
91 | { | ||
92 | unsigned long flags; | ||
93 | struct ftrace_ops **p; | ||
94 | int ret = 0; | ||
95 | |||
96 | spin_lock_irqsave(&ftrace_func_lock, flags); | ||
97 | |||
98 | /* | ||
99 | * If we are the only function, then the ftrace pointer is | ||
100 | * pointing directly to that function. | ||
101 | */ | ||
102 | if (ftrace_list == ops && ops->next == &ftrace_list_end) { | ||
103 | ftrace_trace_function = ftrace_stub; | ||
104 | ftrace_list = &ftrace_list_end; | ||
105 | goto out; | ||
106 | } | ||
107 | |||
108 | for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next) | ||
109 | if (*p == ops) | ||
110 | break; | ||
111 | |||
112 | if (*p != ops) { | ||
113 | ret = -1; | ||
114 | goto out; | ||
115 | } | ||
116 | |||
117 | *p = (*p)->next; | ||
118 | |||
119 | /* If we only have one func left, then call that directly */ | ||
120 | if (ftrace_list->next == &ftrace_list_end) | ||
121 | ftrace_trace_function = ftrace_list->func; | ||
122 | |||
123 | out: | ||
124 | spin_unlock_irqrestore(&ftrace_func_lock, flags); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * clear_ftrace_function - reset the ftrace function | ||
131 | * | ||
132 | * This NULLs the ftrace function and in essence stops | ||
133 | * tracing. There may be lag | ||
134 | */ | ||
135 | void clear_ftrace_function(void) | ||
136 | { | ||
137 | ftrace_trace_function = ftrace_stub; | ||
138 | } | ||
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d2099f41aa1e..d8b6279a9b42 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -634,6 +634,8 @@ config LATENCYTOP | |||
634 | Enable this option if you want to use the LatencyTOP tool | 634 | Enable this option if you want to use the LatencyTOP tool |
635 | to find out which userspace is blocking on what kernel operations. | 635 | to find out which userspace is blocking on what kernel operations. |
636 | 636 | ||
637 | source kernel/trace/Kconfig | ||
638 | |||
637 | config PROVIDE_OHCI1394_DMA_INIT | 639 | config PROVIDE_OHCI1394_DMA_INIT |
638 | bool "Remote debugging over FireWire early on boot" | 640 | bool "Remote debugging over FireWire early on boot" |
639 | depends on PCI && X86 | 641 | depends on PCI && X86 |