Commit d75f054a2cf0614ff63d534ff21ca8eaab41e713
Committed by
Kyle McMartin
1 parent
803094f480
Exists in
master
and in
7 other branches
parisc: add ftrace (function and graph tracer) functionality
This patch adds the ftrace debugging functionality to the parisc kernel. It will currently only work with 64bit kernels, because the gcc options -pg and -ffunction-sections can't be enabled at the same time and -ffunction-sections is still needed to be able to link 32bit kernels. Signed-off-by: Helge Deller <deller@gmx.de> Signed-off-by: Kyle McMartin <kyle@mcmartin.ca>
Showing 11 changed files with 269 additions and 4 deletions Side-by-side Diff
- arch/parisc/Kconfig
- arch/parisc/Makefile
- arch/parisc/include/asm/ftrace.h
- arch/parisc/kernel/Makefile
- arch/parisc/kernel/entry.S
- arch/parisc/kernel/ftrace.c
- arch/parisc/kernel/parisc_ksyms.c
- arch/parisc/kernel/smp.c
- arch/parisc/kernel/time.c
- arch/parisc/kernel/traps.c
- arch/parisc/kernel/vmlinux.lds.S
arch/parisc/Kconfig
... | ... | @@ -9,6 +9,9 @@ |
9 | 9 | def_bool y |
10 | 10 | select HAVE_IDE |
11 | 11 | select HAVE_OPROFILE |
12 | + select HAVE_FUNCTION_TRACER if 64BIT | |
13 | + select HAVE_FUNCTION_GRAPH_TRACER if 64BIT | |
14 | + select HAVE_FUNCTION_TRACE_MCOUNT_TEST if 64BIT | |
12 | 15 | select RTC_CLASS |
13 | 16 | select RTC_DRV_PARISC |
14 | 17 | select INIT_ALL_POSSIBLE |
arch/parisc/Makefile
... | ... | @@ -56,7 +56,9 @@ |
56 | 56 | |
57 | 57 | # Without this, "ld -r" results in .text sections that are too big |
58 | 58 | # (> 0x40000) for branches to reach stubs. |
59 | -cflags-y += -ffunction-sections | |
59 | +ifndef CONFIG_FUNCTION_TRACER | |
60 | + cflags-y += -ffunction-sections | |
61 | +endif | |
60 | 62 | |
61 | 63 | # select which processor to optimise for |
62 | 64 | cflags-$(CONFIG_PA7100) += -march=1.1 -mschedule=7100 |
arch/parisc/include/asm/ftrace.h
1 | +#ifndef _ASM_PARISC_FTRACE_H | |
2 | +#define _ASM_PARISC_FTRACE_H | |
3 | + | |
4 | +#ifndef __ASSEMBLY__ | |
5 | +extern void mcount(void); | |
6 | + | |
7 | +/* | |
8 | + * Stack of return addresses for functions of a thread. | |
9 | + * Used in struct thread_info | |
10 | + */ | |
11 | +struct ftrace_ret_stack { | |
12 | + unsigned long ret; | |
13 | + unsigned long func; | |
14 | + unsigned long long calltime; | |
15 | +}; | |
16 | + | |
17 | +/* | |
18 | + * Primary handler of a function return. | |
19 | + * It relays on ftrace_return_to_handler. | |
20 | + * Defined in entry.S | |
21 | + */ | |
22 | +extern void return_to_handler(void); | |
23 | +#endif /* __ASSEMBLY__ */ | |
24 | + | |
25 | +#endif /* _ASM_PARISC_FTRACE_H */ |
arch/parisc/kernel/Makefile
... | ... | @@ -11,6 +11,18 @@ |
11 | 11 | process.o processor.o pdc_cons.o pdc_chassis.o unwind.o \ |
12 | 12 | topology.o |
13 | 13 | |
14 | +ifdef CONFIG_FUNCTION_TRACER | |
15 | +# Do not profile debug and lowlevel utilities | |
16 | +CFLAGS_REMOVE_ftrace.o = -pg | |
17 | +CFLAGS_REMOVE_cache.o = -pg | |
18 | +CFLAGS_REMOVE_irq.o = -pg | |
19 | +CFLAGS_REMOVE_pacache.o = -pg | |
20 | +CFLAGS_REMOVE_perf.o = -pg | |
21 | +CFLAGS_REMOVE_traps.o = -pg | |
22 | +CFLAGS_REMOVE_unaligned.o = -pg | |
23 | +CFLAGS_REMOVE_unwind.o = -pg | |
24 | +endif | |
25 | + | |
14 | 26 | obj-$(CONFIG_SMP) += smp.o |
15 | 27 | obj-$(CONFIG_PA11) += pci-dma.o |
16 | 28 | obj-$(CONFIG_PCI) += pci.o |
... | ... | @@ -19,4 +31,6 @@ |
19 | 31 | obj-$(CONFIG_STACKTRACE)+= stacktrace.o |
20 | 32 | # only supported for PCX-W/U in 64-bit mode at the moment |
21 | 33 | obj-$(CONFIG_64BIT) += perf.o perf_asm.o |
34 | +obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o | |
35 | +obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o |
arch/parisc/kernel/entry.S
... | ... | @@ -2185,6 +2185,33 @@ |
2185 | 2185 | ENDPROC(syscall_exit) |
2186 | 2186 | |
2187 | 2187 | |
2188 | +#ifdef CONFIG_FUNCTION_TRACER | |
2189 | + .import ftrace_function_trampoline,code | |
2190 | +ENTRY(_mcount) | |
2191 | + copy %r3, %arg2 | |
2192 | + b ftrace_function_trampoline | |
2193 | + nop | |
2194 | +ENDPROC(_mcount) | |
2195 | + | |
2196 | +ENTRY(return_to_handler) | |
2197 | + load32 return_trampoline, %rp | |
2198 | + copy %ret0, %arg0 | |
2199 | + copy %ret1, %arg1 | |
2200 | + b ftrace_return_to_handler | |
2201 | + nop | |
2202 | +return_trampoline: | |
2203 | + copy %ret0, %rp | |
2204 | + copy %r23, %ret0 | |
2205 | + copy %r24, %ret1 | |
2206 | + | |
2207 | +.globl ftrace_stub | |
2208 | +ftrace_stub: | |
2209 | + bv %r0(%rp) | |
2210 | + nop | |
2211 | +ENDPROC(return_to_handler) | |
2212 | +#endif /* CONFIG_FUNCTION_TRACER */ | |
2213 | + | |
2214 | + | |
2188 | 2215 | get_register: |
2189 | 2216 | /* |
2190 | 2217 | * get_register is used by the non access tlb miss handlers to |
arch/parisc/kernel/ftrace.c
1 | +/* | |
2 | + * Code for tracing calls in Linux kernel. | |
3 | + * Copyright (C) 2009 Helge Deller <deller@gmx.de> | |
4 | + * | |
5 | + * based on code for x86 which is: | |
6 | + * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com> | |
7 | + * | |
8 | + * future possible enhancements: | |
9 | + * - add CONFIG_DYNAMIC_FTRACE | |
10 | + * - add CONFIG_STACK_TRACER | |
11 | + */ | |
12 | + | |
13 | +#include <linux/init.h> | |
14 | +#include <linux/ftrace.h> | |
15 | + | |
16 | +#include <asm/sections.h> | |
17 | +#include <asm/ftrace.h> | |
18 | + | |
19 | + | |
20 | + | |
21 | +#ifdef CONFIG_FUNCTION_GRAPH_TRACER | |
22 | + | |
23 | +/* Add a function return address to the trace stack on thread info.*/ | |
24 | +static int push_return_trace(unsigned long ret, unsigned long long time, | |
25 | + unsigned long func, int *depth) | |
26 | +{ | |
27 | + int index; | |
28 | + | |
29 | + if (!current->ret_stack) | |
30 | + return -EBUSY; | |
31 | + | |
32 | + /* The return trace stack is full */ | |
33 | + if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) { | |
34 | + atomic_inc(¤t->trace_overrun); | |
35 | + return -EBUSY; | |
36 | + } | |
37 | + | |
38 | + index = ++current->curr_ret_stack; | |
39 | + barrier(); | |
40 | + current->ret_stack[index].ret = ret; | |
41 | + current->ret_stack[index].func = func; | |
42 | + current->ret_stack[index].calltime = time; | |
43 | + *depth = index; | |
44 | + | |
45 | + return 0; | |
46 | +} | |
47 | + | |
48 | +/* Retrieve a function return address to the trace stack on thread info.*/ | |
49 | +static void pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) | |
50 | +{ | |
51 | + int index; | |
52 | + | |
53 | + index = current->curr_ret_stack; | |
54 | + | |
55 | + if (unlikely(index < 0)) { | |
56 | + ftrace_graph_stop(); | |
57 | + WARN_ON(1); | |
58 | + /* Might as well panic, otherwise we have no where to go */ | |
59 | + *ret = (unsigned long) | |
60 | + dereference_function_descriptor(&panic); | |
61 | + return; | |
62 | + } | |
63 | + | |
64 | + *ret = current->ret_stack[index].ret; | |
65 | + trace->func = current->ret_stack[index].func; | |
66 | + trace->calltime = current->ret_stack[index].calltime; | |
67 | + trace->overrun = atomic_read(¤t->trace_overrun); | |
68 | + trace->depth = index; | |
69 | + barrier(); | |
70 | + current->curr_ret_stack--; | |
71 | + | |
72 | +} | |
73 | + | |
74 | +/* | |
75 | + * Send the trace to the ring-buffer. | |
76 | + * @return the original return address. | |
77 | + */ | |
78 | +unsigned long ftrace_return_to_handler(unsigned long retval0, | |
79 | + unsigned long retval1) | |
80 | +{ | |
81 | + struct ftrace_graph_ret trace; | |
82 | + unsigned long ret; | |
83 | + | |
84 | + pop_return_trace(&trace, &ret); | |
85 | + trace.rettime = cpu_clock(raw_smp_processor_id()); | |
86 | + ftrace_graph_return(&trace); | |
87 | + | |
88 | + if (unlikely(!ret)) { | |
89 | + ftrace_graph_stop(); | |
90 | + WARN_ON(1); | |
91 | + /* Might as well panic. What else to do? */ | |
92 | + ret = (unsigned long) | |
93 | + dereference_function_descriptor(&panic); | |
94 | + } | |
95 | + | |
96 | + /* HACK: we hand over the old functions' return values | |
97 | + in %r23 and %r24. Assembly in entry.S will take care | |
98 | + and move those to their final registers %ret0 and %ret1 */ | |
99 | + asm( "copy %0, %%r23 \n\t" | |
100 | + "copy %1, %%r24 \n" : : "r" (retval0), "r" (retval1) ); | |
101 | + | |
102 | + return ret; | |
103 | +} | |
104 | + | |
105 | +/* | |
106 | + * Hook the return address and push it in the stack of return addrs | |
107 | + * in current thread info. | |
108 | + */ | |
109 | +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) | |
110 | +{ | |
111 | + unsigned long old; | |
112 | + unsigned long long calltime; | |
113 | + struct ftrace_graph_ent trace; | |
114 | + | |
115 | + if (unlikely(atomic_read(¤t->tracing_graph_pause))) | |
116 | + return; | |
117 | + | |
118 | + old = *parent; | |
119 | + *parent = (unsigned long) | |
120 | + dereference_function_descriptor(&return_to_handler); | |
121 | + | |
122 | + if (unlikely(!__kernel_text_address(old))) { | |
123 | + ftrace_graph_stop(); | |
124 | + *parent = old; | |
125 | + WARN_ON(1); | |
126 | + return; | |
127 | + } | |
128 | + | |
129 | + calltime = cpu_clock(raw_smp_processor_id()); | |
130 | + | |
131 | + if (push_return_trace(old, calltime, | |
132 | + self_addr, &trace.depth) == -EBUSY) { | |
133 | + *parent = old; | |
134 | + return; | |
135 | + } | |
136 | + | |
137 | + trace.func = self_addr; | |
138 | + | |
139 | + /* Only trace if the calling function expects to */ | |
140 | + if (!ftrace_graph_entry(&trace)) { | |
141 | + current->curr_ret_stack--; | |
142 | + *parent = old; | |
143 | + } | |
144 | +} | |
145 | + | |
146 | +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | |
147 | + | |
148 | + | |
149 | +void ftrace_function_trampoline(unsigned long parent, | |
150 | + unsigned long self_addr, | |
151 | + unsigned long org_sp_gr3) | |
152 | +{ | |
153 | + extern ftrace_func_t ftrace_trace_function; | |
154 | + | |
155 | + if (function_trace_stop) | |
156 | + return; | |
157 | + | |
158 | + if (ftrace_trace_function != ftrace_stub) { | |
159 | + ftrace_trace_function(parent, self_addr); | |
160 | + return; | |
161 | + } | |
162 | +#ifdef CONFIG_FUNCTION_GRAPH_TRACER | |
163 | + if (ftrace_graph_entry && ftrace_graph_return) { | |
164 | + unsigned long sp; | |
165 | + unsigned long *parent_rp; | |
166 | + | |
167 | + asm volatile ("copy %%r30, %0" : "=r"(sp)); | |
168 | + /* sanity check: is stack pointer which we got from | |
169 | + assembler function in entry.S in a reasonable | |
170 | + range compared to current stack pointer? */ | |
171 | + if ((sp - org_sp_gr3) > 0x400) | |
172 | + return; | |
173 | + | |
174 | + /* calculate pointer to %rp in stack */ | |
175 | + parent_rp = (unsigned long *) org_sp_gr3 - 0x10; | |
176 | + /* sanity check: parent_rp should hold parent */ | |
177 | + if (*parent_rp != parent) | |
178 | + return; | |
179 | + | |
180 | + prepare_ftrace_return(parent_rp, self_addr); | |
181 | + return; | |
182 | + } | |
183 | +#endif | |
184 | +} |
arch/parisc/kernel/parisc_ksyms.c
arch/parisc/kernel/smp.c
... | ... | @@ -31,6 +31,7 @@ |
31 | 31 | #include <linux/err.h> |
32 | 32 | #include <linux/delay.h> |
33 | 33 | #include <linux/bitops.h> |
34 | +#include <linux/ftrace.h> | |
34 | 35 | |
35 | 36 | #include <asm/system.h> |
36 | 37 | #include <asm/atomic.h> |
... | ... | @@ -120,7 +121,7 @@ |
120 | 121 | } |
121 | 122 | |
122 | 123 | |
123 | -irqreturn_t | |
124 | +irqreturn_t __irq_entry | |
124 | 125 | ipi_interrupt(int irq, void *dev_id) |
125 | 126 | { |
126 | 127 | int this_cpu = smp_processor_id(); |
arch/parisc/kernel/time.c
... | ... | @@ -24,6 +24,7 @@ |
24 | 24 | #include <linux/profile.h> |
25 | 25 | #include <linux/clocksource.h> |
26 | 26 | #include <linux/platform_device.h> |
27 | +#include <linux/ftrace.h> | |
27 | 28 | |
28 | 29 | #include <asm/uaccess.h> |
29 | 30 | #include <asm/io.h> |
... | ... | @@ -53,7 +54,7 @@ |
53 | 54 | * held off for an arbitrarily long period of time by interrupts being |
54 | 55 | * disabled, so we may miss one or more ticks. |
55 | 56 | */ |
56 | -irqreturn_t timer_interrupt(int irq, void *dev_id) | |
57 | +irqreturn_t __irq_entry timer_interrupt(int irq, void *dev_id) | |
57 | 58 | { |
58 | 59 | unsigned long now; |
59 | 60 | unsigned long next_tick; |
arch/parisc/kernel/traps.c