Commit d75f054a2cf0614ff63d534ff21ca8eaab41e713

Authored by Helge Deller
Committed by Kyle McMartin
1 parent 803094f480

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

... ... @@ -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(&current->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(&current->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(&current->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
... ... @@ -153,6 +153,11 @@
153 153 EXPORT_SYMBOL(pfnnid_map);
154 154 #endif
155 155  
  156 +#ifdef CONFIG_FUNCTION_TRACER
  157 +extern void _mcount(void);
  158 +EXPORT_SYMBOL(_mcount);
  159 +#endif
  160 +
156 161 /* from pacache.S -- needed for copy_page */
157 162 EXPORT_SYMBOL(copy_user_page_asm);
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
... ... @@ -494,7 +494,7 @@
494 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 499 unsigned long fault_address = 0;
500 500 unsigned long fault_space = 0;
arch/parisc/kernel/vmlinux.lds.S
... ... @@ -54,6 +54,8 @@
54 54 TEXT_TEXT
55 55 SCHED_TEXT
56 56 LOCK_TEXT
  57 + KPROBES_TEXT
  58 + IRQENTRY_TEXT
57 59 *(.text.do_softirq)
58 60 *(.text.sys_exit)
59 61 *(.text.do_sigaltstack)