diff options
| author | Helge Deller <deller@gmx.de> | 2009-02-08 18:43:36 -0500 |
|---|---|---|
| committer | Kyle McMartin <kyle@mcmartin.ca> | 2009-03-30 22:51:34 -0400 |
| commit | d75f054a2cf0614ff63d534ff21ca8eaab41e713 (patch) | |
| tree | e1fbea035711abf72099ebd01918f5ba3c3cf9ae | |
| parent | 803094f480aa5b7dd5187a17e6e60ff24721c212 (diff) | |
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>
| -rw-r--r-- | arch/parisc/Kconfig | 3 | ||||
| -rw-r--r-- | arch/parisc/Makefile | 4 | ||||
| -rw-r--r-- | arch/parisc/include/asm/ftrace.h | 25 | ||||
| -rw-r--r-- | arch/parisc/kernel/Makefile | 14 | ||||
| -rw-r--r-- | arch/parisc/kernel/entry.S | 27 | ||||
| -rw-r--r-- | arch/parisc/kernel/ftrace.c | 185 | ||||
| -rw-r--r-- | arch/parisc/kernel/parisc_ksyms.c | 5 | ||||
| -rw-r--r-- | arch/parisc/kernel/smp.c | 3 | ||||
| -rw-r--r-- | arch/parisc/kernel/time.c | 3 | ||||
| -rw-r--r-- | arch/parisc/kernel/traps.c | 2 | ||||
| -rw-r--r-- | arch/parisc/kernel/vmlinux.lds.S | 2 |
11 files changed, 269 insertions, 4 deletions
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 4866e2f8d3bb..7fd696fd3aef 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig | |||
| @@ -9,6 +9,9 @@ config PARISC | |||
| 9 | def_bool y | 9 | def_bool y |
| 10 | select HAVE_IDE | 10 | select HAVE_IDE |
| 11 | select HAVE_OPROFILE | 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 | select RTC_CLASS | 15 | select RTC_CLASS |
| 13 | select RTC_DRV_PARISC | 16 | select RTC_DRV_PARISC |
| 14 | select INIT_ALL_POSSIBLE | 17 | select INIT_ALL_POSSIBLE |
diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile index 0d428278356d..da6f66901c92 100644 --- a/arch/parisc/Makefile +++ b/arch/parisc/Makefile | |||
| @@ -56,7 +56,9 @@ cflags-y += -mdisable-fpregs | |||
| 56 | 56 | ||
| 57 | # Without this, "ld -r" results in .text sections that are too big | 57 | # Without this, "ld -r" results in .text sections that are too big |
| 58 | # (> 0x40000) for branches to reach stubs. | 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 | # select which processor to optimise for | 63 | # select which processor to optimise for |
| 62 | cflags-$(CONFIG_PA7100) += -march=1.1 -mschedule=7100 | 64 | cflags-$(CONFIG_PA7100) += -march=1.1 -mschedule=7100 |
diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h new file mode 100644 index 000000000000..2fa05dd6aeee --- /dev/null +++ b/arch/parisc/include/asm/ftrace.h | |||
| @@ -0,0 +1,25 @@ | |||
| 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 */ | ||
diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile index a7ba8cd9417f..67db0722e6ca 100644 --- a/arch/parisc/kernel/Makefile +++ b/arch/parisc/kernel/Makefile | |||
| @@ -11,6 +11,18 @@ obj-y := cache.o pacache.o setup.o traps.o time.o irq.o \ | |||
| 11 | process.o processor.o pdc_cons.o pdc_chassis.o unwind.o \ | 11 | process.o processor.o pdc_cons.o pdc_chassis.o unwind.o \ |
| 12 | topology.o | 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 | obj-$(CONFIG_SMP) += smp.o | 26 | obj-$(CONFIG_SMP) += smp.o |
| 15 | obj-$(CONFIG_PA11) += pci-dma.o | 27 | obj-$(CONFIG_PA11) += pci-dma.o |
| 16 | obj-$(CONFIG_PCI) += pci.o | 28 | obj-$(CONFIG_PCI) += pci.o |
| @@ -19,3 +31,5 @@ obj-$(CONFIG_64BIT) += binfmt_elf32.o sys_parisc32.o signal32.o | |||
| 19 | obj-$(CONFIG_STACKTRACE)+= stacktrace.o | 31 | obj-$(CONFIG_STACKTRACE)+= stacktrace.o |
| 20 | # only supported for PCX-W/U in 64-bit mode at the moment | 32 | # only supported for PCX-W/U in 64-bit mode at the moment |
| 21 | obj-$(CONFIG_64BIT) += perf.o perf_asm.o | 33 | obj-$(CONFIG_64BIT) += perf.o perf_asm.o |
| 34 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o | ||
| 35 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o | ||
diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S index 4e9dd15e076c..ae3e70cd1e14 100644 --- a/arch/parisc/kernel/entry.S +++ b/arch/parisc/kernel/entry.S | |||
| @@ -2185,6 +2185,33 @@ syscall_do_resched: | |||
| 2185 | ENDPROC(syscall_exit) | 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 | get_register: | 2215 | get_register: |
| 2189 | /* | 2216 | /* |
| 2190 | * get_register is used by the non access tlb miss handlers to | 2217 | * get_register is used by the non access tlb miss handlers to |
diff --git a/arch/parisc/kernel/ftrace.c b/arch/parisc/kernel/ftrace.c new file mode 100644 index 000000000000..9877372ffdba --- /dev/null +++ b/arch/parisc/kernel/ftrace.c | |||
| @@ -0,0 +1,185 @@ | |||
| 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 | } | ||
| 185 | |||
diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c index 0eecfbbc59cd..df653663d3db 100644 --- a/arch/parisc/kernel/parisc_ksyms.c +++ b/arch/parisc/kernel/parisc_ksyms.c | |||
| @@ -153,5 +153,10 @@ EXPORT_SYMBOL(node_data); | |||
| 153 | EXPORT_SYMBOL(pfnnid_map); | 153 | EXPORT_SYMBOL(pfnnid_map); |
| 154 | #endif | 154 | #endif |
| 155 | 155 | ||
| 156 | #ifdef CONFIG_FUNCTION_TRACER | ||
| 157 | extern void _mcount(void); | ||
| 158 | EXPORT_SYMBOL(_mcount); | ||
| 159 | #endif | ||
| 160 | |||
| 156 | /* from pacache.S -- needed for copy_page */ | 161 | /* from pacache.S -- needed for copy_page */ |
| 157 | EXPORT_SYMBOL(copy_user_page_asm); | 162 | EXPORT_SYMBOL(copy_user_page_asm); |
diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c index 9995d7ed5819..8545f2e7a2c0 100644 --- a/arch/parisc/kernel/smp.c +++ b/arch/parisc/kernel/smp.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/err.h> | 31 | #include <linux/err.h> |
| 32 | #include <linux/delay.h> | 32 | #include <linux/delay.h> |
| 33 | #include <linux/bitops.h> | 33 | #include <linux/bitops.h> |
| 34 | #include <linux/ftrace.h> | ||
| 34 | 35 | ||
| 35 | #include <asm/system.h> | 36 | #include <asm/system.h> |
| 36 | #include <asm/atomic.h> | 37 | #include <asm/atomic.h> |
| @@ -120,7 +121,7 @@ halt_processor(void) | |||
| 120 | } | 121 | } |
| 121 | 122 | ||
| 122 | 123 | ||
| 123 | irqreturn_t | 124 | irqreturn_t __irq_entry |
| 124 | ipi_interrupt(int irq, void *dev_id) | 125 | ipi_interrupt(int irq, void *dev_id) |
| 125 | { | 126 | { |
| 126 | int this_cpu = smp_processor_id(); | 127 | int this_cpu = smp_processor_id(); |
diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c index 9d46c43a4152..badaad9ff139 100644 --- a/arch/parisc/kernel/time.c +++ b/arch/parisc/kernel/time.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/profile.h> | 24 | #include <linux/profile.h> |
| 25 | #include <linux/clocksource.h> | 25 | #include <linux/clocksource.h> |
| 26 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
| 27 | #include <linux/ftrace.h> | ||
| 27 | 28 | ||
| 28 | #include <asm/uaccess.h> | 29 | #include <asm/uaccess.h> |
| 29 | #include <asm/io.h> | 30 | #include <asm/io.h> |
| @@ -53,7 +54,7 @@ static unsigned long clocktick __read_mostly; /* timer cycles per tick */ | |||
| 53 | * held off for an arbitrarily long period of time by interrupts being | 54 | * held off for an arbitrarily long period of time by interrupts being |
| 54 | * disabled, so we may miss one or more ticks. | 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 | unsigned long now; | 59 | unsigned long now; |
| 59 | unsigned long next_tick; | 60 | unsigned long next_tick; |
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index ba658d2086f7..7bf122a5f169 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c | |||
| @@ -494,7 +494,7 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o | |||
| 494 | panic(msg); | 494 | panic(msg); |
| 495 | } | 495 | } |
| 496 | 496 | ||
| 497 | void handle_interruption(int code, struct pt_regs *regs) | 497 | void notrace handle_interruption(int code, struct pt_regs *regs) |
| 498 | { | 498 | { |
| 499 | unsigned long fault_address = 0; | 499 | unsigned long fault_address = 0; |
| 500 | unsigned long fault_space = 0; | 500 | unsigned long fault_space = 0; |
diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S index 1a3b6ccd3620..fd2cc4fd2b65 100644 --- a/arch/parisc/kernel/vmlinux.lds.S +++ b/arch/parisc/kernel/vmlinux.lds.S | |||
| @@ -54,6 +54,8 @@ SECTIONS | |||
| 54 | TEXT_TEXT | 54 | TEXT_TEXT |
| 55 | SCHED_TEXT | 55 | SCHED_TEXT |
| 56 | LOCK_TEXT | 56 | LOCK_TEXT |
| 57 | KPROBES_TEXT | ||
| 58 | IRQENTRY_TEXT | ||
| 57 | *(.text.do_softirq) | 59 | *(.text.do_softirq) |
| 58 | *(.text.sys_exit) | 60 | *(.text.sys_exit) |
| 59 | *(.text.do_sigaltstack) | 61 | *(.text.do_sigaltstack) |
