That thread "MIPS: Add option to pass return address location to
_mcount" from "David Daney <ddaney@caviumnetworks.com>" have added a new
option -mmcount-ra-address to gcc(4.5) for MIPS to transfer the location
of the return address to _mcount.
Benefit from this new feature, function graph tracer on MIPS will be
easier and safer to hijack the return address of the kernel function,
which will save some overhead and make the whole thing more reliable.
In this patch, at first, try to enable the option -mmcount-ra-address in
arch/mips/Makefile with cc-option, if gcc support it, it will be
enabled, otherwise, no side effect.
and then, we need to support this new option of gcc 4.5 and also support
the old gcc versions.
with _mcount in the old gcc versions, it's not easy to get the location
of return address(tracing: add function graph tracer support for MIPS),
so, we do it in a C function: ftrace_get_parent_addr(ftrace.c), but
with -mmcount-ra-address, only several instructions need to get what
we want, so, I put into asm(mcount.S). and also, as the $12(t0) is
used by -mmcount-ra-address for transferring the localtion of return
address to _mcount, we need to save it into the stack and restore it
when enabled dynamic function tracer, 'Cause we have called
"ftrace_call" before "ftrace_graph_caller", which may destroy
$12(t0).
(Thanks to David for providing that -mcount-ra-address and giving the
idea of KBUILD_MCOUNT_RA_ADDRESS, both of them have made the whole
thing more beautiful!)
Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Nicholas Mc Guire <der.herr@hofr.at>
Cc: zhangfx@lemote.com
Cc: Wu Zhangjin <wuzhangjin@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@linux-mips.org
Patchwork: http://patchwork.linux-mips.org/patch/681/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
ifndef CONFIG_FUNCTION_TRACER
cflags-y := -ffunction-sections
endif
ifndef CONFIG_FUNCTION_TRACER
cflags-y := -ffunction-sections
endif
+ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ ifndef KBUILD_MCOUNT_RA_ADDRESS
+ ifeq ($(call cc-option-yn,-mmcount-ra-address), y)
+ cflags-y += -mmcount-ra-address -DKBUILD_MCOUNT_RA_ADDRESS
+ endif
+ endif
+endif
cflags-y += $(call cc-option, -mno-check-zero-division)
ifdef CONFIG_32BIT
cflags-y += $(call cc-option, -mno-check-zero-division)
ifdef CONFIG_32BIT
#endif /* !CONFIG_DYNAMIC_FTRACE */
#endif /* !CONFIG_DYNAMIC_FTRACE */
+#ifndef KBUILD_MCOUNT_RA_ADDRESS
#define S_RA_SP (0xafbf << 16) /* s{d,w} ra, offset(sp) */
#define S_R_SP (0xafb0 << 16) /* s{d,w} R, offset(sp) */
#define OFFSET_MASK 0xffff /* stack offset range: 0 ~ PT_SIZE */
#define S_RA_SP (0xafbf << 16) /* s{d,w} ra, offset(sp) */
#define S_R_SP (0xafb0 << 16) /* s{d,w} R, offset(sp) */
#define OFFSET_MASK 0xffff /* stack offset range: 0 ~ PT_SIZE */
/*
* Hook the return address and push it in the stack of return addrs
* in current thread info.
/*
* Hook the return address and push it in the stack of return addrs
* in current thread info.
return;
/* "parent" is the stack address saved the return address of the caller
return;
/* "parent" is the stack address saved the return address of the caller
- * of _mcount, for a leaf function not save the return address in the
- * stack address, so, we "emulate" one in _mcount's stack space, and
- * hijack it directly, but for a non-leaf function, it will save the
- * return address to the its stack space, so, we can not hijack the
- * "parent" directly, but need to find the real stack address,
+ * of _mcount.
+ *
+ * if the gcc < 4.5, a leaf function does not save the return address
+ * in the stack address, so, we "emulate" one in _mcount's stack space,
+ * and hijack it directly, but for a non-leaf function, it save the
+ * return address to the its own stack space, we can not hijack it
+ * directly, but need to find the real stack address,
* ftrace_get_parent_addr() does it!
* ftrace_get_parent_addr() does it!
+ *
+ * if gcc>= 4.5, with the new -mmcount-ra-address option, for a
+ * non-leaf function, the location of the return address will be saved
+ * to $12 for us, and for a leaf function, only put a zero into $12. we
+ * do it in ftrace_graph_caller of mcount.S.
*/
/* old = *parent; */
safe_load_stack(old, parent, faulted);
if (unlikely(faulted))
goto out;
*/
/* old = *parent; */
safe_load_stack(old, parent, faulted);
if (unlikely(faulted))
goto out;
+#ifndef KBUILD_MCOUNT_RA_ADDRESS
parent = (unsigned long *)ftrace_get_parent_addr(self_addr, old,
(unsigned long)parent,
fp);
parent = (unsigned long *)ftrace_get_parent_addr(self_addr, old,
(unsigned long)parent,
fp);
* ra, stop function graph tracer and return */
if (parent == 0)
goto out;
* ra, stop function graph tracer and return */
if (parent == 0)
goto out;
/* *parent = return_hooker; */
safe_store_stack(return_hooker, parent, faulted);
if (unlikely(faulted))
/* *parent = return_hooker; */
safe_store_stack(return_hooker, parent, faulted);
if (unlikely(faulted))
+#ifdef KBUILD_MCOUNT_RA_ADDRESS
+ PTR_S t0, PT_R12(sp) /* t0 saved the location of the return address(at) by -mmcount-ra-address */
+#endif
move a0, ra /* arg1: next ip, selfaddr */
.globl ftrace_call
move a0, ra /* arg1: next ip, selfaddr */
.globl ftrace_call
NESTED(ftrace_graph_caller, PT_SIZE, ra)
#ifdef CONFIG_DYNAMIC_FTRACE
PTR_L a1, PT_R31(sp) /* load the original ra from the stack */
NESTED(ftrace_graph_caller, PT_SIZE, ra)
#ifdef CONFIG_DYNAMIC_FTRACE
PTR_L a1, PT_R31(sp) /* load the original ra from the stack */
+#ifdef KBUILD_MCOUNT_RA_ADDRESS
+ PTR_L t0, PT_R12(sp) /* load the original t0 from the stack */
+#endif
#else
MCOUNT_SAVE_REGS
move a1, ra /* arg2: next ip, selfaddr */
#endif
#else
MCOUNT_SAVE_REGS
move a1, ra /* arg2: next ip, selfaddr */
#endif
+
+#ifdef KBUILD_MCOUNT_RA_ADDRESS
+ bnez t0, 1f /* non-leaf func: t0 saved the location of the return address */
+ nop
+ PTR_LA t0, PT_R1(sp) /* leaf func: get the location of at(old ra) from our own stack */
+1: move a0, t0 /* arg1: the location of the return address */
+#else
PTR_LA a0, PT_R1(sp) /* arg1: &AT -> a0 */
PTR_LA a0, PT_R1(sp) /* arg1: &AT -> a0 */
jal prepare_ftrace_return
#ifdef CONFIG_FRAME_POINTER
move a2, fp /* arg3: frame pointer */
jal prepare_ftrace_return
#ifdef CONFIG_FRAME_POINTER
move a2, fp /* arg3: frame pointer */