From d75f054a2cf0614ff63d534ff21ca8eaab41e713 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 9 Feb 2009 00:43:36 +0100 Subject: 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 Signed-off-by: Kyle McMartin --- arch/parisc/kernel/ftrace.c | 185 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 arch/parisc/kernel/ftrace.c (limited to 'arch/parisc/kernel/ftrace.c') 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 @@ +/* + * Code for tracing calls in Linux kernel. + * Copyright (C) 2009 Helge Deller + * + * based on code for x86 which is: + * Copyright (C) 2007-2008 Steven Rostedt + * + * future possible enhancements: + * - add CONFIG_DYNAMIC_FTRACE + * - add CONFIG_STACK_TRACER + */ + +#include +#include + +#include +#include + + + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + +/* Add a function return address to the trace stack on thread info.*/ +static int push_return_trace(unsigned long ret, unsigned long long time, + unsigned long func, int *depth) +{ + int index; + + if (!current->ret_stack) + return -EBUSY; + + /* The return trace stack is full */ + if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) { + atomic_inc(¤t->trace_overrun); + return -EBUSY; + } + + index = ++current->curr_ret_stack; + barrier(); + current->ret_stack[index].ret = ret; + current->ret_stack[index].func = func; + current->ret_stack[index].calltime = time; + *depth = index; + + return 0; +} + +/* Retrieve a function return address to the trace stack on thread info.*/ +static void pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) +{ + int index; + + index = current->curr_ret_stack; + + if (unlikely(index < 0)) { + ftrace_graph_stop(); + WARN_ON(1); + /* Might as well panic, otherwise we have no where to go */ + *ret = (unsigned long) + dereference_function_descriptor(&panic); + return; + } + + *ret = current->ret_stack[index].ret; + trace->func = current->ret_stack[index].func; + trace->calltime = current->ret_stack[index].calltime; + trace->overrun = atomic_read(¤t->trace_overrun); + trace->depth = index; + barrier(); + current->curr_ret_stack--; + +} + +/* + * Send the trace to the ring-buffer. + * @return the original return address. + */ +unsigned long ftrace_return_to_handler(unsigned long retval0, + unsigned long retval1) +{ + struct ftrace_graph_ret trace; + unsigned long ret; + + pop_return_trace(&trace, &ret); + trace.rettime = cpu_clock(raw_smp_processor_id()); + ftrace_graph_return(&trace); + + if (unlikely(!ret)) { + ftrace_graph_stop(); + WARN_ON(1); + /* Might as well panic. What else to do? */ + ret = (unsigned long) + dereference_function_descriptor(&panic); + } + + /* HACK: we hand over the old functions' return values + in %r23 and %r24. Assembly in entry.S will take care + and move those to their final registers %ret0 and %ret1 */ + asm( "copy %0, %%r23 \n\t" + "copy %1, %%r24 \n" : : "r" (retval0), "r" (retval1) ); + + return ret; +} + +/* + * Hook the return address and push it in the stack of return addrs + * in current thread info. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) +{ + unsigned long old; + unsigned long long calltime; + struct ftrace_graph_ent trace; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + + old = *parent; + *parent = (unsigned long) + dereference_function_descriptor(&return_to_handler); + + if (unlikely(!__kernel_text_address(old))) { + ftrace_graph_stop(); + *parent = old; + WARN_ON(1); + return; + } + + calltime = cpu_clock(raw_smp_processor_id()); + + if (push_return_trace(old, calltime, + self_addr, &trace.depth) == -EBUSY) { + *parent = old; + return; + } + + trace.func = self_addr; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + current->curr_ret_stack--; + *parent = old; + } +} + +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ + + +void ftrace_function_trampoline(unsigned long parent, + unsigned long self_addr, + unsigned long org_sp_gr3) +{ + extern ftrace_func_t ftrace_trace_function; + + if (function_trace_stop) + return; + + if (ftrace_trace_function != ftrace_stub) { + ftrace_trace_function(parent, self_addr); + return; + } +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + if (ftrace_graph_entry && ftrace_graph_return) { + unsigned long sp; + unsigned long *parent_rp; + + asm volatile ("copy %%r30, %0" : "=r"(sp)); + /* sanity check: is stack pointer which we got from + assembler function in entry.S in a reasonable + range compared to current stack pointer? */ + if ((sp - org_sp_gr3) > 0x400) + return; + + /* calculate pointer to %rp in stack */ + parent_rp = (unsigned long *) org_sp_gr3 - 0x10; + /* sanity check: parent_rp should hold parent */ + if (*parent_rp != parent) + return; + + prepare_ftrace_return(parent_rp, self_addr); + return; + } +#endif +} + -- cgit v1.2.3