This is my second version of patchset for ftrace support. Actually v1 was submitted serveral weeks ago, but is still moderated. (Just ignore them for now.) There is another implementation from Cavium network, but both works are independent, and my code has additional system call trace support.
I confirmed that I could compile the patches on v3.12-rc4 by Linaro's coming 2013.10 gcc (4.8.2), and that the kernel worked on Fast Model with the following tracers: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint irqsoff & preemptirqsoff (which use CALLER_ADDRx) Also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Patch[3/6] has warnings from checkpatch, but they follow other arch's style.
Please be careful that host's elf.h must have AArch64 definitions, EM_AARCH64 and R_AARCH64_ABS64, to build the kernel. See [4/6].
Issues * Can we optimize register usages in asm (by not saving x0, x1 and x2)? [1/6] * Do we need "fault protection" code in ftrace_modify_code()? [1/6] It exists in x86 and other architectures, but not in arm. * We may be able to use aarch64_insn_patch_text_nosync() instead of ftrace_modify_code().[2/6] But the former function does not use probe_kernel_write(). Is this safe?
Changes from v1 to v2: * splitted one patch into some pieces for easier review (especially function tracer + dynamic ftrace + CALLER_ADDRx) * put return_address() in a separate file * renamed __mcount to _mcount (it was my mistake) * changed stackframe handling to get parent's frame pointer * removed ARCH_SUPPORTS_FTRACE_OPS * switched to "hotpatch" interfaces from Huawai * revised descriptions in comments
AKASHI Takahiro (6): arm64: Add ftrace support arm64: ftrace: Add dynamic ftrace support arm64: ftrace: Add CALLER_ADDRx macros ftrace: Add arm64 support to recordmcount arm64: ftrace: Add system call tracepoint arm64: Add 'notrace' attribute to unwind_frame() for ftrace
arch/arm64/Kconfig | 6 + arch/arm64/include/asm/ftrace.h | 54 +++++++++ arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/thread_info.h | 1 + arch/arm64/include/asm/unistd.h | 2 + arch/arm64/kernel/Makefile | 9 +- arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 211 ++++++++++++++++++++++++++++++++++ arch/arm64/kernel/entry.S | 1 + arch/arm64/kernel/ftrace.c | 186 ++++++++++++++++++++++++++++++ arch/arm64/kernel/ptrace.c | 10 ++ arch/arm64/kernel/return_address.c | 55 +++++++++ arch/arm64/kernel/stacktrace.c | 2 +- scripts/recordmcount.c | 4 + scripts/recordmcount.pl | 5 + 15 files changed, 549 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c create mode 100644 arch/arm64/kernel/return_address.c
This enables FUNCTION_TRACER and FUNCTION_GRAPH_TRACER, and also provides the base for other tracers which depend on FUNCTION_TRACER.
_mcount() is the entry point which is inserted at the very beginning of every function by gcc with -pg option. function graph tracer intercepts instrumented function's return path by faking the return address (lr) stored in stack in order to trace a call graph.
See Documentation/trace/ftrace-design.txt
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/ftrace.h | 23 +++++ arch/arm64/kernel/Makefile | 6 ++ arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 172 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 83 ++++++++++++++++++ 6 files changed, 290 insertions(+) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index da388e4..3776319 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -23,6 +23,8 @@ config ARM64 select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..0d5dfdb --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/* + * arch/arm64/include/asm/ftrace.h + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H + +#ifdef CONFIG_FUNCTION_TRACER +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ + +#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_FUNCTION_TRACER */ + +#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index b7db65e..92429e4 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,11 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+ifdef CONFIG_FUNCTION_TRACER +CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg +endif + # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ @@ -13,6 +18,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o +arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o smp_psci.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 41b4f62..ef9b63d 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -58,3 +58,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..ae14ece --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,172 @@ +/* + * arch/arm64/kernel/entry-ftrace.S + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/ftrace.h> + +/* + * Gcc with -pg will put the following code in the beginning of each function: + * mov x0, x30 + * bl _mcount + * On contrary to tricky arm(32) implementation, this is a normal function + * call and so x0 & x30 will be safely saved and restored around tracer + * call (_mcount/ftrace_caller) in an instrumented function (callsite). + * + * stack layout: + * 0 ---------------------------------------- sp in tracer function + * x29: fp in instrumented function fp is not winded + * -------------------- + * x30: lr in tracer function + * +16 -------------------- + * x0: arg 0 (lr in instrumented function) + * -------------------- + * x1 (temporary) + * +32 -------------------- + * x2 (temporary) + * -------------------- + * (don't care) + * +48 ---------------------------------------- sp in instrumented function + * + * .... + * + * +xx ---------------------------------------- fp in instrumented function + * x29: fp in parent function + * -------------------- + * x30: lr in insturmented function + * -------------------- + * xxx + */ + + .macro mcount_enter + stp x29, x30, [sp, #-48]! + stp x0, x1, [sp, #16] + str x2, [sp, #32] + .endm + + .macro mcount_exit + ldr x2, [sp, #32] + ldp x0, x1, [sp, #16] + ldp x29, x30, [sp], #48 + ret + .endm + + .macro mcount_adjust_addr rd, rn + sub \rd, \rn, #MCOUNT_INSN_SIZE + .endm + + /* for instrumented function's parent */ + .macro mcount_get_parent_fp reg + ldr \reg, [sp] + ldr \reg, [\reg] + .endm + + /* for instrumented function */ + .macro mcount_get_pc0 reg + mcount_adjust_addr \reg, x30 + .endm + + .macro mcount_get_pc reg + ldr \reg, [sp, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr reg + ldr \reg, [sp, #16] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_saved_lr_addr reg + ldr \reg, [sp] + add \reg, \reg, #8 + .endm + +/* + * void _mcount(unsigned long return_address) + * @return_address: return address to instrumented function (callsite) + */ +ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST + ldr x0, =ftrace_trace_stop + ldr x0, [x0] // if ftrace_trace_stop + ret // return; +#endif + mcount_enter + + ldr x0, =ftrace_trace_function + ldr x2, [x0] + adr x0, ftrace_stub + cmp x0, x2 // if (ftrace_trace_function + b.eq skip_ftrace_call // != ftrace_stub) { + + mcount_get_pc x0 // pc in callsite + mcount_get_lr x1 // callsite's lr (adjusted) + blr x2 // (*ftrace_trace_function)(pc, lr); + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call: // return; + mcount_exit // } +#else + mcount_exit // return; + // } +skip_ftrace_call: + ldr x1, =ftrace_graph_return + ldr x2, [x1] // if ((ftrace_graph_return + cmp x0, x2 // != ftrace_stub) + b.ne ftrace_graph_caller + + ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry + ldr x2, [x1] // != ftrace_graph_entry_stub)) + ldr x0, =ftrace_graph_entry_stub + cmp x0, x2 + b.ne ftrace_graph_caller // ftrace_graph_caller(); + + mcount_exit +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount) + +ENTRY(ftrace_stub) + ret +ENDPROC(ftrace_stub) + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * void ftrace_graph_caller(void) + * + * This function fakes instrumented function's return address to make a hook + * on function return path by calling prepare_ftrace_return(). This function + * is assumed to be jumped into from _mcount() or ftrace_caller() and so no + * context need be saved here. + */ +ENTRY(ftrace_graph_caller) + mcount_get_saved_lr_addr x0 // pointer to callsite's saved lr + mcount_get_pc x1 // pc in callsite + mcount_get_parent_fp x2 // parent's fp + bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp) + + mcount_exit +ENDPROC(ftrace_graph_caller) + +/* + * void return_to_handler(void) + * + * return hook handler + * @fp is used to check against the value specified in ftrace_graph_caller() + * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled. + */ + .global return_to_handler +return_to_handler: + str x0, [sp, #-16]! + mov x0, x29 // parent's fp + bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); + mov x30, x0 // restore the original return address + ldr x0, [sp], #16 + ret +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..e779e16 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,83 @@ +/* + * arch/arm64/kernel/ftrace.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + unsigned long frame_pointer) +{ + unsigned long return_hooker = (unsigned long)&return_to_handler; + unsigned long old, faulted; + struct ftrace_graph_ent trace; + int err; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + +#if 1 /* FIXME */ + /* + * Protect against fault, even if it shouldn't + * happen. This tool is too much intrusive to + * ignore such a protection. + * Actually we want to do + * old = *parent; + * parent = return_hooker; + */ + asm volatile( +"1: ldr %0, [%2]\n" +"2: str %3, [%2]\n" +" mov %1, #0\n" +"3:\n" +" .pushsection .fixup, "ax"\n" +"4: mov %1, #1\n" +" b 3b\n" +" .popsection\n" +" .pushsection __ex_table, "a"\n" +" .align 3\n" +" .quad 1b, 4b, 2b, 4b\n" +" .popsection\n" + : "=&r" (old), "=r" (faulted) : "r" (parent), "r" (return_hooker) + ); + + if (unlikely(faulted)) { + ftrace_graph_stop(); + WARN_ON(1); + return; + } +#else + old = *parent; + *parent = return_hooker; +#endif + + trace.func = self_addr; + trace.depth = current->curr_ret_stack + 1; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + *parent = old; + return; + } + + err = ftrace_push_return_trace(old, self_addr, &trace.depth, + frame_pointer); + if (err == -EBUSY) { + *parent = old; + return; + } +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
This enables DYNAMIC_FTRACE configuration. ftrace_caller() is the entry function which is a replacement of _mcount(). If tracing is enabled on a function, the branch instruction to _mcount() is replaced to that of ftace_caller() and if disabled, replaced to nop.
See Documentation/trace/ftrace-design.txt
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 17 +++++++ arch/arm64/kernel/entry-ftrace.S | 39 +++++++++++++++ arch/arm64/kernel/ftrace.c | 103 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 3776319..c150430 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -23,6 +23,7 @@ config ARM64 select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS + select HAVE_DYNAMIC_FTRACE select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 0d5dfdb..37a379d 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -17,6 +17,23 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); + +#ifdef CONFIG_DYNAMIC_FTRACE +struct dyn_arch_ftrace { + /* No extra data needed for arm64 */ +}; + +extern unsigned long ftrace_graph_call; + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ + /* + * addr is the address of the mcount call instruction. + * recordmcount does the necessary offset calculation. + */ + return addr; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* __ASSEMBLY__ */ #endif /* CONFIG_FUNCTION_TRACER */
diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S index ae14ece..c35c650 100644 --- a/arch/arm64/kernel/entry-ftrace.S +++ b/arch/arm64/kernel/entry-ftrace.S @@ -88,6 +88,7 @@ add \reg, \reg, #8 .endm
+#ifndef CONFIG_DYNAMIC_FTRACE /* * void _mcount(unsigned long return_address) * @return_address: return address to instrumented function (callsite) @@ -132,6 +133,44 @@ skip_ftrace_call: #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ENDPROC(_mcount)
+#else /* CONFIG_DYNAMIC_FTRACE */ +/* + * This code is executed only at boot time. + * Once after initialized, "bl _mcount" generated by gcc -pg option will be + * replaced to "nop" if tracing is disabled or "b ftrace_caller" if enabled. + */ +ENTRY(_mcount) + ret +ENDPROC(_mcount) + +/* + * void ftrace_caller(unsigned long return_address) + * @return_address: return address to instrumented function (callsite) + * + * Call any kind of tracer function and/or function graph tracer. + */ +ENTRY(ftrace_caller) + mcount_enter + + mcount_get_pc0 x0 // pc in callsite + mcount_get_lr x1 // callsite's lr (adjusted) + + .global ftrace_call +ftrace_call: // tracer(pc, lr); + nop // This will be replaced with "bl xxx" + // where xxx can be any kind of tracer. + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + .global ftrace_graph_call +ftrace_graph_call: // ftrace_graph_caller(); + nop // If enabled, this will be replaced + // "b ftrace_graph_caller" +#endif + + mcount_exit +ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */ + ENTRY(ftrace_stub) ret ENDPROC(ftrace_stub) diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index e779e16..da481c1 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -17,6 +17,76 @@ #include <asm/ftrace.h> #include <asm/insn.h>
+#ifdef CONFIG_DYNAMIC_FTRACE +/* FIXME: can be replaced by aarch64_insn_patch_text_nosync() */ +static int ftrace_modify_code(unsigned long pc, unsigned int old, + unsigned int new, bool validate) +{ + unsigned int replaced; + +#ifdef __AARCH64EB__ + old = swab32(old); + new = swab32(new); +#endif + + if (validate) { + if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE)) + return -EFAULT; + + if (replaced != old) + return -EINVAL; + } + + if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE)) + return -EPERM; + + flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); + + return 0; +} + +int ftrace_update_ftrace_func(ftrace_func_t func) +{ + unsigned long pc; + unsigned int new; + + pc = (unsigned long)&ftrace_call; + new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); + + return ftrace_modify_code(pc, 0, new, false); +} + +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_nop(); + new = aarch64_insn_gen_branch_imm(pc, addr, true); + + return ftrace_modify_code(pc, old, new, true); +} + +int ftrace_make_nop(struct module *mod, + struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_branch_imm(pc, addr, true); + new = aarch64_insn_gen_nop(); + + return ftrace_modify_code(pc, old, new, true); +} + +int __init ftrace_dyn_arch_init(void *data) +{ + *(unsigned long *)data = 0; + + return 0; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + #ifdef CONFIG_FUNCTION_GRAPH_TRACER void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, unsigned long frame_pointer) @@ -80,4 +150,37 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } } + +#ifdef CONFIG_DYNAMIC_FTRACE +static int __ftrace_modify_caller(unsigned long *callsite, + void (*func) (void), bool enable) +{ + unsigned long pc = (unsigned long)callsite; + unsigned int branch, nop, old, new; + + branch = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, false); + nop = aarch64_insn_gen_nop(); + old = enable ? nop : branch; + new = enable ? branch : nop; + + return ftrace_modify_code(pc, old, new, true); +} + +static int ftrace_modify_graph_caller(bool enable) +{ + return __ftrace_modify_caller(&ftrace_graph_call, + ftrace_graph_caller, + enable); +} + +int ftrace_enable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(true); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(false); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
These macro's unwind call stacks and return the specified caller address. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please not that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/ftrace.h | 14 +++++++++ arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 37a379d..4c6ac03 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -37,4 +37,18 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) #endif /* __ASSEMBLY__ */ #endif /* CONFIG_FUNCTION_TRACER */
+#ifndef __ASSEMBLY__ +#define HAVE_ARCH_CALLER_ADDR + +extern void *return_address(unsigned int); + +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#endif /* ifndef __ASSEMBLY__ */ + #endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 92429e4..94d1876 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -9,12 +9,13 @@ ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_insn.o = -pg endif +CFLAGS_REMOVE_return_address.o = -pg
# Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ - hyp-stub.o psci.o insn.o + hyp-stub.o psci.o insn.o return_address.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 0000000..89102a6 --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/* + * arch/arm64/kernel/return_address.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/export.h> +#include <linux/ftrace.h> + +#include <asm/stacktrace.h> + +struct return_address_data { + unsigned int level; + void *addr; +}; + +static int save_return_addr(struct stackframe *frame, void *d) +{ + struct return_address_data *data = d; + + if (!data->level) { + data->addr = (void *)frame->pc; + return 1; + } else { + --data->level; + return 0; + } +} + +void *return_address(unsigned int level) +{ + struct return_address_data data; + struct stackframe frame; + register unsigned long current_sp asm ("sp"); + + data.level = level + 2; + data.addr = NULL; + + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.pc = (unsigned long)return_address; /* dummy */ + + walk_stackframe(&frame, save_return_addr, &data); + + if (!data.level) + return data.addr; + else + return NULL; +} +EXPORT_SYMBOL_GPL(return_address);
Recordmcount utility scans every object, finds out all the locations of calling _mcount() and puts them into __mcount_loc section. linker compiles all the information in between __start_mcount_loc and __stop_mcount_loc for later use by ftrace.
There are two types of implementation, C or Perl. In arm64, only C version is used since HAVE_C_RECORDMCOUNT is defined.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 ++ scripts/recordmcount.c | 4 ++++ scripts/recordmcount.pl | 5 +++++ 3 files changed, 11 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index c150430..4f4a0dc 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -19,11 +19,13 @@ config ARM64 select HARDIRQS_SW_RESEND select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_TRACEHOOK + select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DYNAMIC_FTRACE + select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 9c22317..b92d9f7 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -347,6 +347,10 @@ do_file(char const *const fname) case EM_ARM: reltype = R_ARM_ABS32; altmcount = "__gnu_mcount_nc"; break; + case EM_AARCH64: + reltype = R_AARCH64_ABS64; + altmcount = "_mcount"; + break; case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; case EM_METAG: reltype = R_METAG_ADDR32; altmcount = "_mcount_wrapper"; diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index a674fd5..ae8b0fe 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$";
+} elsif ($arch eq "arm64") { + $alignment = 3; + $section_type = '%progbits'; + $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$"; + $type = ".quad"; } elsif ($arch eq "ia64") { $mcount_regex = "^\s*([0-9a-fA-F]+):.*\s_mcount$"; $type = "data8";
This enables FTRACE_SYSCALLS configuration. Entry and/or exit to any system call can be traced as a ftrace event.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/thread_info.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/entry.S | 1 + arch/arm64/kernel/ptrace.c | 10 ++++++++++ 6 files changed, 16 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 4f4a0dc..f01d84e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -32,6 +32,7 @@ config ARM64 select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK select HAVE_PERF_EVENTS + select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@
#include <linux/err.h>
+extern const void *sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index 23a3c47..6f5d977 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -107,6 +107,7 @@ static inline struct thread_info *current_thread_info(void) #define TIF_NEED_RESCHED 1 #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ #define TIF_SYSCALL_TRACE 8 +#define TIF_SYSCALL_TRACEPOINT 10 #define TIF_POLLING_NRFLAG 16 #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ #define TIF_FREEZE 19 diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h> + +#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 3881fd1..7f81bfe 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -646,6 +646,7 @@ el0_svc_naked: // compat entry point get_thread_info tsk ldr x16, [tsk, #TI_FLAGS] // check for syscall tracing tbnz x16, #TIF_SYSCALL_TRACE, __sys_trace // are we tracing syscalls? + tbnz x16, #TIF_SYSCALL_TRACEPOINT, __sys_trace adr lr, ret_fast_syscall // return address cmp scno, sc_nr // check upper syscall limit b.hs ni_sys diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index fecdbf7..548baa3 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -41,6 +41,9 @@ #include <asm/traps.h> #include <asm/system_misc.h>
+#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> + /* * TODO: does not yet catch signals sent when the child dies. * in exit.c or in signal.c. @@ -1066,6 +1069,13 @@ asmlinkage int syscall_trace(int dir, struct pt_regs *regs) { unsigned long saved_reg;
+ if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) { + if (dir) + trace_sys_exit(regs, regs->syscallno); + else + trace_sys_enter(regs, regs->syscallno); + } + if (!test_thread_flag(TIF_SYSCALL_TRACE)) return regs->syscallno;
walk_stackframe() calls unwind_frame(), and if walk_stackframe() is "notrace", unwind_frame() should be also "notrace".
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index d25459f..03dd5a2 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -35,7 +35,7 @@ * ldp x29, x30, [sp] * add sp, sp, #0x10 */ -int unwind_frame(struct stackframe *frame) +int notrace unwind_frame(struct stackframe *frame) { unsigned long high, low; unsigned long fp = frame->fp;
This is my third version of patchset for ftrace support. There was another implementation from Cavium network, but both of us agreed to use my patchset as future base. He is supposed to review this code, too.
The only issue that I had some concern on was "fault protection" code in prepare_ftrace_return(). With discussions with Steven and Tim (as author of arm ftrace), I removed that code since I'm not quite sure about possibility of "fault" occurrences in this function.
The code is tested on ARMv8 Fast Model with the following tracers & events: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint irqsoff & preemptirqsoff (which use CALLER_ADDRx) and also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Please be careful: * elf.h on cross-build host must have AArch64 definitions, EM_AARCH64 and R_AARCH64_ABS64, to compile recordmcount utility. See [4/6]. [4/6] also gets warnings from checkpatch, but they are based on the original's coding style. * This patch may conflict with my audit patch because both changes the same location in syscall_trace(). I expect the functions are called in this order: On entry, * tracehook_report_syscall(ENTER) * trace_sys_enter() * audit_syscall_entry() On exit, * audit_sysscall_exit() * trace_sys_exit() * tracehook_report_syscall(EXIT)
Changes from v1 to v2: * splitted one patch into some pieces for easier review (especially function tracer + dynamic ftrace + CALLER_ADDRx) * put return_address() in a separate file * renamed __mcount to _mcount (it was my mistake) * changed stackframe handling to get parent's frame pointer * removed ARCH_SUPPORTS_FTRACE_OPS * switched to "hotpatch" interfaces from Huawai * revised descriptions in comments
Changes from v2 to v3: * optimized register usages in asm (by not saving x0, x1, and x2) * removed "fault protection" code in prepare_ftrace_return() * rewrote ftrace_modify_code() using "hotpatch" interfaces * revised descriptions in comments
AKASHI Takahiro (6): arm64: Add ftrace support arm64: ftrace: Add dynamic ftrace support arm64: ftrace: Add CALLER_ADDRx macros ftrace: Add arm64 support to recordmcount arm64: ftrace: Add system call tracepoint arm64: Add 'notrace' attribute to unwind_frame() for ftrace
arch/arm64/Kconfig | 6 + arch/arm64/include/asm/ftrace.h | 54 +++++++++ arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 + arch/arm64/kernel/Makefile | 9 +- arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 215 ++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 177 +++++++++++++++++++++++++++++ arch/arm64/kernel/ptrace.c | 5 + arch/arm64/kernel/return_address.c | 55 +++++++++ arch/arm64/kernel/stacktrace.c | 2 +- scripts/recordmcount.c | 4 + scripts/recordmcount.pl | 5 + 13 files changed, 537 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c create mode 100644 arch/arm64/kernel/return_address.c
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/ftrace.h | 23 +++++ arch/arm64/kernel/Makefile | 6 ++ arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 172 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 64 ++++++++++++++ 6 files changed, 271 insertions(+) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index dd4327f..332e50c 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -33,6 +33,8 @@ config ARM64 select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..0d5dfdb --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/* + * arch/arm64/include/asm/ftrace.h + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H + +#ifdef CONFIG_FUNCTION_TRACER +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ + +#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_FUNCTION_TRACER */ + +#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ca921fb 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,11 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+ifdef CONFIG_FUNCTION_TRACER +CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg +endif + # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ @@ -13,6 +18,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o +arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..065f426 --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,172 @@ +/* + * arch/arm64/kernel/entry-ftrace.S + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/ftrace.h> + +/* + * Gcc with -pg will put the following code in the beginning of each function: + * mov x0, x30 + * bl _mcount + * [function's body ...] + * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic + * ftrace is enabled. + * + * Please note that x0 as an argument will not be used here because we can + * get lr(x30) of insturmented function at any time by winding up call stack + * as long as the kernel is compiled without -fomit-frame-pointer. + * (or CONFIG_FRAME_POINTER, this is forced on arm64) + * + * stack layout after mcount_enter: + * + * 0 ---------------------------------------- sp of _mcount() + * x29: fp of instrumented function fp is not winded + * -------------------- + * x30: lr of _mcount() (= instrumented function's pc) + * +16 ---------------------------------------- sp of instrumented function + * + * .... + * + * +xx ---------------------------------------- fp of instrumented function + * x29: fp of parent + * -------------------- + * x30: lr of insturmented function (= parent's pc) + * -------------------- + * xxx + */ + + .macro mcount_enter + stp x29, x30, [sp, #-48]! + .endm + + .macro mcount_exit + ldp x29, x30, [sp], #48 + ret + .endm + + .macro mcount_adjust_addr rd, rn + sub \rd, \rn, #MCOUNT_INSN_SIZE + .endm + + /* for instrumented function's parent */ + .macro mcount_get_parent_fp reg + ldr \reg, [sp] + ldr \reg, [\reg] + .endm + + /* for instrumented function */ + .macro mcount_get_pc0 reg + mcount_adjust_addr \reg, x30 + .endm + + .macro mcount_get_pc reg + ldr \reg, [sp, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr reg + ldr \reg, [sp] + ldr \reg, [\reg, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_saved_lr_addr reg + ldr \reg, [sp] + add \reg, \reg, #8 + .endm + +/* + * void _mcount(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function makes calls, if enabled, to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST + ldr x0, =ftrace_trace_stop + ldr x0, [x0] // if ftrace_trace_stop + ret // return; +#endif + mcount_enter + + ldr x0, =ftrace_trace_function + ldr x2, [x0] + adr x0, ftrace_stub + cmp x0, x2 // if (ftrace_trace_function + b.eq skip_ftrace_call // != ftrace_stub) { + + mcount_get_pc x0 // function's pc + mcount_get_lr x1 // function's lr (= parent's pc) + blr x2 // (*ftrace_trace_function)(pc, lr); + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call: // return; + mcount_exit // } +#else + mcount_exit // return; + // } +skip_ftrace_call: + ldr x1, =ftrace_graph_return + ldr x2, [x1] // if ((ftrace_graph_return + cmp x0, x2 // != ftrace_stub) + b.ne ftrace_graph_caller + + ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry + ldr x2, [x1] // != ftrace_graph_entry_stub)) + ldr x0, =ftrace_graph_entry_stub + cmp x0, x2 + b.ne ftrace_graph_caller // ftrace_graph_caller(); + + mcount_exit +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount) + +ENTRY(ftrace_stub) + ret +ENDPROC(ftrace_stub) + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * void ftrace_graph_caller(void) + * + * Called from _mcount() or ftrace_caller() when function_graph tracer is + * selected. + * This function w/ prepare_ftrace_return() fakes link register's value on + * the call stack in order to intercept instrumented function's return path + * and run return_to_handler() later on its exit. + */ +ENTRY(ftrace_graph_caller) + mcount_get_saved_lr_addr x0 // pointer to function's saved lr + mcount_get_pc x1 // function's pc + mcount_get_parent_fp x2 // parent's fp + bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp) + + mcount_exit +ENDPROC(ftrace_graph_caller) + +/* + * void return_to_handler(void) + * + * Run ftrace_return_to_handler() before going back to parent. + * @fp is checked against the value passed by ftrace_graph_caller() + * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled. + */ + .global return_to_handler +return_to_handler: + str x0, [sp, #-16]! + mov x0, x29 // parent's fp + bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); + mov x30, x0 // restore the original return address + ldr x0, [sp], #16 + ret +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/* + * arch/arm64/kernel/ftrace.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * function_graph tracer expects ftrace_return_to_handler() to be called + * on the way back to parent. For this purpose, this function is called + * in _mcount() or ftrace_caller() to replace return address (*parent) on + * the call stack to return_to_handler. + * + * Note that @frame_pointer is used only for sanity check later. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + unsigned long frame_pointer) +{ + unsigned long return_hooker = (unsigned long)&return_to_handler; + unsigned long old; + struct ftrace_graph_ent trace; + int err; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + + /* + * Note: + * No protection against faulting at *parent, which may be seen + * on other archs. It's unlikely on AArch64. + */ + old = *parent; + *parent = return_hooker; + + trace.func = self_addr; + trace.depth = current->curr_ret_stack + 1; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + *parent = old; + return; + } + + err = ftrace_push_return_trace(old, self_addr, &trace.depth, + frame_pointer); + if (err == -EBUSY) { + *parent = old; + return; + } +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
On Friday 07 February 2014, AKASHI Takahiro wrote:
@@ -0,0 +1,23 @@
+#ifdef CONFIG_FUNCTION_TRACER +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
+#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_FUNCTION_TRACER */
We generally like to have as few #ifdef as possible, and I think the #ifdef CONFIG_FUNCTION_TRACER here is not needed.
+#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ca921fb 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,11 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) +ifdef CONFIG_FUNCTION_TRACER +CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg +endif
Same here. It never hurts to have the CFLAGS_REMOVE_* statements here, since you will not want to build these files for profiling.
diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit);
+#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif
This one is clearly needed of course.
+/*
- Gcc with -pg will put the following code in the beginning of each function:
mov x0, x30
bl _mcount
- [function's body ...]
- "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic
- ftrace is enabled.
Unrelated to your patch, I have run into problems with this on arm32, maybe someone on Cc to this mail has an idea what to do about it:
If I build a large "randconfig" kernel or "allyesconfig", the kernel image size exceeds 32MB, which means I can no longer link callers with a 26 bit signed immediate argument like the "bl _mcount" here. For any other function calls, "gcc -mlong-calls" can be used to work around this, but this particular instance is created by inserting assembly statements into the output without considering the long-call options. Is there a way to get the kernel to link anyway?
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
Here, again, you an remove the #ifdef by making the entire file built only for CONFIG_FUNCTION_GRAPH_TRACER.
Arnd
On Mon, 10 Feb 2014 16:03:21 +0100 Arnd Bergmann arnd@arndb.de wrote:
On Friday 07 February 2014, AKASHI Takahiro wrote:
@@ -0,0 +1,23 @@
+#ifdef CONFIG_FUNCTION_TRACER +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
+#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_FUNCTION_TRACER */
We generally like to have as few #ifdef as possible, and I think the #ifdef CONFIG_FUNCTION_TRACER here is not needed.
+#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ca921fb 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,11 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) +ifdef CONFIG_FUNCTION_TRACER +CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg +endif
Same here. It never hurts to have the CFLAGS_REMOVE_* statements here, since you will not want to build these files for profiling.
I agree to this. I will admit, this was probably just a copy from my code as I did the same in other Makefiles. I think I added the ifdef statements as more of documenting what the REMOVE was for, when ftrace was just being added to the kernel, and not well known.
I should probably go back and remove the ifdef's from other Makefiles too.
A comment about what the -pg is for wouldn't hurt, though:
# For files that should not have any function tracing done to them, # we must remove the -pg flag.
Something like that.
diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit);
+#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif
This one is clearly needed of course.
+/*
- Gcc with -pg will put the following code in the beginning of each function:
mov x0, x30
bl _mcount
- [function's body ...]
- "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic
- ftrace is enabled.
Unrelated to your patch, I have run into problems with this on arm32, maybe someone on Cc to this mail has an idea what to do about it:
If I build a large "randconfig" kernel or "allyesconfig", the kernel image size exceeds 32MB, which means I can no longer link callers with a 26 bit signed immediate argument like the "bl _mcount" here. For any other function calls, "gcc -mlong-calls" can be used to work around this, but this particular instance is created by inserting assembly statements into the output without considering the long-call options. Is there a way to get the kernel to link anyway?
I wonder if we play linker games and move the _mcount and ftrace_caller and friends into the middle of the kernel? If it is only missing it by a slight overflow of a 32bit jump, then maybe moving it will work.
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
Here, again, you an remove the #ifdef by making the entire file built only for CONFIG_FUNCTION_GRAPH_TRACER.
Interesting. Other archs (at least x86 and powerpc) required this file for function tracing too. Seems this is only needed for function graph tracer.
-- Steve
On 11 February 2014 00:03, Arnd Bergmann arnd@arndb.de wrote:
On Friday 07 February 2014, AKASHI Takahiro wrote:
@@ -0,0 +1,23 @@
+#ifdef CONFIG_FUNCTION_TRACER +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
+#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_FUNCTION_TRACER */
We generally like to have as few #ifdef as possible, and I think the #ifdef CONFIG_FUNCTION_TRACER here is not needed.
I will remove it.
+#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ca921fb 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,11 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+ifdef CONFIG_FUNCTION_TRACER +CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg +endif
Same here. It never hurts to have the CFLAGS_REMOVE_* statements here,
since
you will not want to build these files for profiling.
I will remove it.
diff --git a/arch/arm64/kernel/arm64ksyms.c
b/arch/arm64/kernel/arm64ksyms.c
index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit);
+#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif
This one is clearly needed of course.
Sure.
+/*
- Gcc with -pg will put the following code in the beginning of each
function:
mov x0, x30
bl _mcount
- [function's body ...]
- "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic
- ftrace is enabled.
Unrelated to your patch, I have run into problems with this on arm32,
maybe
someone on Cc to this mail has an idea what to do about it:
If I build a large "randconfig" kernel or "allyesconfig", the kernel image size exceeds 32MB, which means I can no longer link callers with a 26 bit signed immediate argument like the "bl _mcount" here. For any other function calls, "gcc -mlong-calls" can be used to work around this, but this particular instance is created by inserting assembly statements into the output without considering the long-call options. Is there a way to get the kernel to link anyway?
I hope that this won't happen on arm64 because a branch offset range is +/- 128MB.
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
Here, again, you an remove the #ifdef by making the entire file built only for CONFIG_FUNCTION_GRAPH_TRACER.
You're right as far as patch [1/6] is concerned, but once patch [2/6] is applied, this file (ftrace.c) may be compiled even if CONFIG_FUNCTION_GRAPH_TRACER is not enabled. (That is CONFIG_DYNAMIC_FTRACE.) Sorry for any confusion by poorly-divided patchset.
-Takahiro AKASHI
Arnd
On Wednesday 12 February 2014, Takahiro Akashi wrote:
If I build a large "randconfig" kernel or "allyesconfig", the kernel image size exceeds 32MB, which means I can no longer link callers with a 26 bit signed immediate argument like the "bl _mcount" here. For any other function calls, "gcc -mlong-calls" can be used to work around this, but this particular instance is created by inserting assembly statements into the output without considering the long-call options. Is there a way to get the kernel to link anyway?
I hope that this won't happen on arm64 because a branch offset range is +/- 128MB.
Yes, we should be safe for the next decade on arm64. My question was really about arm32 here, hoping someone has an idea.
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
Here, again, you an remove the #ifdef by making the entire file built only for CONFIG_FUNCTION_GRAPH_TRACER.
You're right as far as patch [1/6] is concerned, but once patch [2/6] is applied, this file (ftrace.c) may be compiled even if CONFIG_FUNCTION_GRAPH_TRACER is not enabled. (That is CONFIG_DYNAMIC_FTRACE.) Sorry for any confusion by poorly-divided patchset.
No, that was my fault. I saw it later after I had already sent out the message.
Arnd
On Fri, Feb 07, 2014 at 10:18:51AM +0000, AKASHI Takahiro wrote:
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
[...]
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..0d5dfdb --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/*
- arch/arm64/include/asm/ftrace.h
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H
+#ifdef CONFIG_FUNCTION_TRACER +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
You can use AARCH64_INSN_SIZE here.
+#include <linux/linkage.h> +#include <asm/ftrace.h>
+/*
- Gcc with -pg will put the following code in the beginning of each function:
mov x0, x30
bl _mcount
[function's body ...]
- "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic
- ftrace is enabled.
- Please note that x0 as an argument will not be used here because we can
- get lr(x30) of insturmented function at any time by winding up call stack
instrumented
- as long as the kernel is compiled without -fomit-frame-pointer.
- (or CONFIG_FRAME_POINTER, this is forced on arm64)
- stack layout after mcount_enter:
- 0 ---------------------------------------- sp of _mcount()
x29: fp of instrumented function fp is not winded
--------------------
x30: lr of _mcount() (= instrumented function's pc)
- +16 ---------------------------------------- sp of instrumented function
....
- +xx ---------------------------------------- fp of instrumented function
x29: fp of parent
--------------------
x30: lr of insturmented function (= parent's pc)
--------------------
xxx
- */
.macro mcount_enter
stp x29, x30, [sp, #-48]!
.endm
Can you elaborate in your comment about where this 48 comes from please? I can't join it up with your ascii art.
Will
On 02/18/2014 03:12 AM, Will Deacon wrote:
On Fri, Feb 07, 2014 at 10:18:51AM +0000, AKASHI Takahiro wrote:
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
[...]
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..0d5dfdb --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/*
- arch/arm64/include/asm/ftrace.h
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H
+#ifdef CONFIG_FUNCTION_TRACER +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
You can use AARCH64_INSN_SIZE here.
OK, but MCOUNT_INSN_SIZE can't be removed here because it is also used in kernel/trace/ftrace.c. So I will redefine it as #define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE
In this case, I need to add __ASSEMBLY__ in asm/insn.h because asm/ftrace.h (and so asm/insn.h) is included in entry-ftrace.S.
Is it OK?
+#include <linux/linkage.h> +#include <asm/ftrace.h>
+/*
- Gcc with -pg will put the following code in the beginning of each function:
mov x0, x30
bl _mcount
[function's body ...]
- "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic
- ftrace is enabled.
- Please note that x0 as an argument will not be used here because we can
- get lr(x30) of insturmented function at any time by winding up call stack
instrumented
I will fix two occurences of "insturmented" :-) Just a question: Is there any better word than "instrument"?
- as long as the kernel is compiled without -fomit-frame-pointer.
- (or CONFIG_FRAME_POINTER, this is forced on arm64)
- stack layout after mcount_enter:
- 0 ---------------------------------------- sp of _mcount()
x29: fp of instrumented function fp is not winded
--------------------
x30: lr of _mcount() (= instrumented function's pc)
- +16 ---------------------------------------- sp of instrumented function
....
- +xx ---------------------------------------- fp of instrumented function
x29: fp of parent
--------------------
x30: lr of insturmented function (= parent's pc)
--------------------
xxx
- */
.macro mcount_enter
stp x29, x30, [sp, #-48]!
.endm
Can you elaborate in your comment about where this 48 comes from please? I can't join it up with your ascii art.
Right. It should be -16 as shown in the stack layout. When I removed an operation of saving/restoring x0-x3 at v3 patch, this value should also have been changed.
-Takahiro AKASHI
Will
This patch allows "dynamic ftrace" if CONFIG_DYNAMIC_FTRACE is enabled. Here we can turn on and off tracing dynamically per-function base.
On arm64, this is done by patching single branch instruction to _mcount() inserted by gcc -pg option. The branch is replaced to NOP initially at kernel start up, and later on, NOP to branch to ftrace_caller() when enabled or branch to NOP when disabled. Please note that ftrace_caller() is a counterpart of _mcount() in case of 'static' ftrace.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 17 ++++++ arch/arm64/kernel/entry-ftrace.S | 43 +++++++++++++++ arch/arm64/kernel/ftrace.c | 113 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 332e50c..4d81791 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -32,6 +32,7 @@ config ARM64 select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS + select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 0d5dfdb..37a379d 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -17,6 +17,23 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); + +#ifdef CONFIG_DYNAMIC_FTRACE +struct dyn_arch_ftrace { + /* No extra data needed for arm64 */ +}; + +extern unsigned long ftrace_graph_call; + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ + /* + * addr is the address of the mcount call instruction. + * recordmcount does the necessary offset calculation. + */ + return addr; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* __ASSEMBLY__ */ #endif /* CONFIG_FUNCTION_TRACER */
diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S index 065f426..d8dd298 100644 --- a/arch/arm64/kernel/entry-ftrace.S +++ b/arch/arm64/kernel/entry-ftrace.S @@ -83,6 +83,7 @@ add \reg, \reg, #8 .endm
+#ifndef CONFIG_DYNAMIC_FTRACE /* * void _mcount(unsigned long return_address) * @return_address: return address to instrumented function @@ -131,6 +132,48 @@ skip_ftrace_call: #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ENDPROC(_mcount)
+#else /* CONFIG_DYNAMIC_FTRACE */ +/* + * _mcount() is used to build the kernel with -pg option, but all the branch + * instructions to _mcount() are replaced to NOP initially at kernel start up, + * and later on, NOP to branch to ftrace_caller() when enabled or branch to + * NOP when disabled per-function base. + */ +ENTRY(_mcount) + ret +ENDPROC(_mcount) + +/* + * void ftrace_caller(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function is a counterpart of _mcount() in 'static' ftrace, and + * makes calls to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(ftrace_caller) + mcount_enter + + mcount_get_pc0 x0 // function's pc + mcount_get_lr x1 // function's lr + + .global ftrace_call +ftrace_call: // tracer(pc, lr); + nop // This will be replaced with "bl xxx" + // where xxx can be any kind of tracer. + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + .global ftrace_graph_call +ftrace_graph_call: // ftrace_graph_caller(); + nop // If enabled, this will be replaced + // "b ftrace_graph_caller" +#endif + + mcount_exit +ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */ + ENTRY(ftrace_stub) ret ENDPROC(ftrace_stub) diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index a559ab8..8c26476 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -17,6 +17,89 @@ #include <asm/ftrace.h> #include <asm/insn.h>
+#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Replace a single instruction, which may be a branch or NOP. + * If @validate == true, a replaced instruction is checked against 'old'. + */ +static int ftrace_modify_code(unsigned long pc, unsigned int old, + unsigned int new, bool validate) +{ + unsigned int replaced; + + /* + * Note: + * Due to modules and __init, code can disappear and change, + * we need to protect against faulting as well as code changing. + * We do this by aarch64_insn_*() which use the probe_kernel_*(). + * + * No lock is held here because all the modifications are run + * through stop_machine(). + */ + if (validate) { + if (aarch64_insn_read((void *)pc, &replaced)) + return -EFAULT; + + if (replaced != old) + return -EINVAL; + } + if (aarch64_insn_patch_text_nosync((void *)pc, new)) + return -EPERM; + + return 0; +} + +/* + * Replace tracer function in ftrace_caller() + */ +int ftrace_update_ftrace_func(ftrace_func_t func) +{ + unsigned long pc; + unsigned int new; + + pc = (unsigned long)&ftrace_call; + new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); + + return ftrace_modify_code(pc, 0, new, false); +} + +/* + * Turn on the call to ftrace_caller() in instrumented function + */ +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_nop(); + new = aarch64_insn_gen_branch_imm(pc, addr, true); + + return ftrace_modify_code(pc, old, new, true); +} + +/* + * Turn off the call to ftrace_caller() in instrumented function + */ +int ftrace_make_nop(struct module *mod, + struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_branch_imm(pc, addr, true); + new = aarch64_insn_gen_nop(); + + return ftrace_modify_code(pc, old, new, true); +} + +int __init ftrace_dyn_arch_init(void *data) +{ + *(unsigned long *)data = 0; + + return 0; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* * function_graph tracer expects ftrace_return_to_handler() to be called @@ -61,4 +144,34 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } } + +#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Turn on/off the call to ftrace_graph_caller() in ftrace_caller() + * depending on @enable. + */ +static int ftrace_modify_graph_caller(bool enable) +{ + unsigned long pc = (unsigned long)&ftrace_graph_call; + unsigned int branch, nop, old, new; + + branch = aarch64_insn_gen_branch_imm(pc, + (unsigned long)ftrace_graph_caller, false); + nop = aarch64_insn_gen_nop(); + old = enable ? nop : branch; + new = enable ? branch : nop; + + return ftrace_modify_code(pc, old, new, true); +} + +int ftrace_enable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(true); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(false); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/ftrace.h | 14 +++++++++ arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 37a379d..4c6ac03 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -37,4 +37,18 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) #endif /* __ASSEMBLY__ */ #endif /* CONFIG_FUNCTION_TRACER */
+#ifndef __ASSEMBLY__ +#define HAVE_ARCH_CALLER_ADDR + +extern void *return_address(unsigned int); + +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#endif /* ifndef __ASSEMBLY__ */ + #endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index ca921fb..55e0b6e 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -9,12 +9,13 @@ ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_insn.o = -pg endif +CFLAGS_REMOVE_return_address.o = -pg
# Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ - hyp-stub.o psci.o cpu_ops.o insn.o + hyp-stub.o psci.o cpu_ops.o insn.o return_address.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 0000000..89102a6 --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/* + * arch/arm64/kernel/return_address.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/export.h> +#include <linux/ftrace.h> + +#include <asm/stacktrace.h> + +struct return_address_data { + unsigned int level; + void *addr; +}; + +static int save_return_addr(struct stackframe *frame, void *d) +{ + struct return_address_data *data = d; + + if (!data->level) { + data->addr = (void *)frame->pc; + return 1; + } else { + --data->level; + return 0; + } +} + +void *return_address(unsigned int level) +{ + struct return_address_data data; + struct stackframe frame; + register unsigned long current_sp asm ("sp"); + + data.level = level + 2; + data.addr = NULL; + + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.pc = (unsigned long)return_address; /* dummy */ + + walk_stackframe(&frame, save_return_addr, &data); + + if (!data.level) + return data.addr; + else + return NULL; +} +EXPORT_SYMBOL_GPL(return_address);
Recordmcount utility under scripts is run, after compiling each object, to find out all the locations of calling _mcount() and put them into specific seciton named __mcount_loc. Then linker collects all such information into a table in the kernel image (between __start_mcount_loc and __stop_mcount_loc) for later use by ftrace.
This patch adds arm64 specific definitions to identify such locations. There are two types of implementation, C and Perl. On arm64, only C version is used to build the kernel now that CONFIG_HAVE_C_RECORDMCOUNT is on. But Perl version is also maintained.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 ++ scripts/recordmcount.c | 4 ++++ scripts/recordmcount.pl | 5 +++++ 3 files changed, 11 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 4d81791..43fce76 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -27,6 +27,7 @@ config ARM64 select HARDIRQS_SW_RESEND select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_TRACEHOOK + select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG @@ -34,6 +35,7 @@ config ARM64 select HAVE_DMA_CONTIGUOUS select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 9c22317..b92d9f7 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -347,6 +347,10 @@ do_file(char const *const fname) case EM_ARM: reltype = R_ARM_ABS32; altmcount = "__gnu_mcount_nc"; break; + case EM_AARCH64: + reltype = R_AARCH64_ABS64; + altmcount = "_mcount"; + break; case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; case EM_METAG: reltype = R_METAG_ADDR32; altmcount = "_mcount_wrapper"; diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 91280b8..397b6b8 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$";
+} elsif ($arch eq "arm64") { + $alignment = 3; + $section_type = '%progbits'; + $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$"; + $type = ".quad"; } elsif ($arch eq "ia64") { $mcount_regex = "^\s*([0-9a-fA-F]+):.*\s_mcount$"; $type = "data8";
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 5 +++++ 4 files changed, 9 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 43fce76..9bdaf5c 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -42,6 +42,7 @@ config ARM64 select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK select HAVE_PERF_EVENTS + select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@
#include <linux/err.h>
+extern const void *sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h> + +#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 64ce39f..f4c3c4e 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -41,6 +41,9 @@ #include <asm/traps.h> #include <asm/system_misc.h>
+#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> + /* * TODO: does not yet catch signals sent when the child dies. * in exit.c or in signal.c. @@ -1076,10 +1079,12 @@ asmlinkage int syscall_trace(int dir, struct pt_regs *regs) }
if (dir) { + trace_sys_exit(regs, regs->syscallno); tracehook_report_syscall_exit(regs, 0); } else { if (tracehook_report_syscall_entry(regs)) regs->syscallno = ~0UL; + trace_sys_enter(regs, regs->syscallno); }
if (is_compat_task())
On Fri, Feb 07, 2014 at 10:18:55AM +0000, AKASHI Takahiro wrote:
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/Kconfig | 1 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 5 +++++ 4 files changed, 9 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 43fce76..9bdaf5c 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -42,6 +42,7 @@ config ARM64 select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK select HAVE_PERF_EVENTS
- select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM
diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@ #include <linux/err.h> +extern const void *sys_call_table[]; static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h>
+#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 64ce39f..f4c3c4e 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -41,6 +41,9 @@ #include <asm/traps.h> #include <asm/system_misc.h> +#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h>
/*
- TODO: does not yet catch signals sent when the child dies.
- in exit.c or in signal.c.
@@ -1076,10 +1079,12 @@ asmlinkage int syscall_trace(int dir, struct pt_regs *regs) } if (dir) {
tracehook_report_syscall_exit(regs, 0); } else { if (tracehook_report_syscall_entry(regs)) regs->syscallno = ~0UL;trace_sys_exit(regs, regs->syscallno);
trace_sys_enter(regs, regs->syscallno);
Shouldn't these calls be guarded by a TIF_SYSCALL_TRACEPOINT check?
Will
On 02/18/2014 04:29 AM, Will Deacon wrote:
On Fri, Feb 07, 2014 at 10:18:55AM +0000, AKASHI Takahiro wrote:
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/Kconfig | 1 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 5 +++++ 4 files changed, 9 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 43fce76..9bdaf5c 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -42,6 +42,7 @@ config ARM64 select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK select HAVE_PERF_EVENTS
- select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM
diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@
#include <linux/err.h>
+extern const void *sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h>
+#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 64ce39f..f4c3c4e 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -41,6 +41,9 @@ #include <asm/traps.h> #include <asm/system_misc.h>
+#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h>
- /*
- TODO: does not yet catch signals sent when the child dies.
- in exit.c or in signal.c.
@@ -1076,10 +1079,12 @@ asmlinkage int syscall_trace(int dir, struct pt_regs *regs) }
if (dir) {
tracehook_report_syscall_exit(regs, 0); } else { if (tracehook_report_syscall_entry(regs)) regs->syscallno = ~0UL;trace_sys_exit(regs, regs->syscallno);
trace_sys_enter(regs, regs->syscallno);
Shouldn't these calls be guarded by a TIF_SYSCALL_TRACEPOINT check?
Yes. I will add the guard.
-Takahiro AKASHI
Will
walk_stackframe() calls unwind_frame(), and if walk_stackframe() is "notrace", unwind_frame() should be also "notrace".
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index c3b6c63..54122c4 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -35,7 +35,7 @@ * ldp x29, x30, [sp] * add sp, sp, #0x10 */ -int unwind_frame(struct stackframe *frame) +int notrace unwind_frame(struct stackframe *frame) { unsigned long high, low; unsigned long fp = frame->fp;
Looks OK to me.
Reviewed-by: Ganapatrao Kulkarni ganapatrao.kulkarni@cavium.com
regards, Ganapat
________________________________________ From: AKASHI Takahiro takahiro.akashi@linaro.org Sent: Friday, February 7, 2014 3:48 PM To: rostedt@goodmis.org; fweisbec@gmail.com; mingo@redhat.com; catalin.marinas@arm.com; will.deacon@arm.com; Kulkarni, Ganapatrao; tim.bird@sonymobile.com Cc: arndb@arndb.de; linux-arm-kernel@lists.infradead.org; linaro-kernel@lists.linaro.org; linux-kernel@vger.kernel.org; patches@linaro.org; AKASHI Takahiro Subject: [PATCH v3 0/6] arm64: Add ftrace support
This is my third version of patchset for ftrace support. There was another implementation from Cavium network, but both of us agreed to use my patchset as future base. He is supposed to review this code, too.
The only issue that I had some concern on was "fault protection" code in prepare_ftrace_return(). With discussions with Steven and Tim (as author of arm ftrace), I removed that code since I'm not quite sure about possibility of "fault" occurrences in this function.
The code is tested on ARMv8 Fast Model with the following tracers & events: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint irqsoff & preemptirqsoff (which use CALLER_ADDRx) and also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Please be careful: * elf.h on cross-build host must have AArch64 definitions, EM_AARCH64 and R_AARCH64_ABS64, to compile recordmcount utility. See [4/6]. [4/6] also gets warnings from checkpatch, but they are based on the original's coding style. * This patch may conflict with my audit patch because both changes the same location in syscall_trace(). I expect the functions are called in this order: On entry, * tracehook_report_syscall(ENTER) * trace_sys_enter() * audit_syscall_entry() On exit, * audit_sysscall_exit() * trace_sys_exit() * tracehook_report_syscall(EXIT)
Changes from v1 to v2: * splitted one patch into some pieces for easier review (especially function tracer + dynamic ftrace + CALLER_ADDRx) * put return_address() in a separate file * renamed __mcount to _mcount (it was my mistake) * changed stackframe handling to get parent's frame pointer * removed ARCH_SUPPORTS_FTRACE_OPS * switched to "hotpatch" interfaces from Huawai * revised descriptions in comments
Changes from v2 to v3: * optimized register usages in asm (by not saving x0, x1, and x2) * removed "fault protection" code in prepare_ftrace_return() * rewrote ftrace_modify_code() using "hotpatch" interfaces * revised descriptions in comments
AKASHI Takahiro (6): arm64: Add ftrace support arm64: ftrace: Add dynamic ftrace support arm64: ftrace: Add CALLER_ADDRx macros ftrace: Add arm64 support to recordmcount arm64: ftrace: Add system call tracepoint arm64: Add 'notrace' attribute to unwind_frame() for ftrace
arch/arm64/Kconfig | 6 + arch/arm64/include/asm/ftrace.h | 54 +++++++++ arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 + arch/arm64/kernel/Makefile | 9 +- arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 215 ++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 177 +++++++++++++++++++++++++++++ arch/arm64/kernel/ptrace.c | 5 + arch/arm64/kernel/return_address.c | 55 +++++++++ arch/arm64/kernel/stacktrace.c | 2 +- scripts/recordmcount.c | 4 + scripts/recordmcount.pl | 5 + 13 files changed, 537 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c create mode 100644 arch/arm64/kernel/return_address.c
-- 1.7.9.5
This patchset implements a function tracer on arm64. There was another implementation from Cavium network, but both of us agreed to use my patchset as future base. He is supposed to review this code, too.
The only issue that I had some concern on was "fault protection" code in prepare_ftrace_return(). With discussions with Steven and Tim (as author of arm ftrace), I removed that code since I'm not quite sure about possibility of "fault" occurrences in this function.
The code is tested on ARMv8 Fast Model with the following tracers & events: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint irqsoff & preemptirqsoff (which use CALLER_ADDRx) and also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Prerequisites are: * "arm64: Add regs_return_value() in syscall.h" patch included in "arm64: Add audit support" patchset * "arm64: make a single hook to syscall_trace() for all syscall features" patch
Please be careful: * elf.h on cross-build host must have AArch64 definitions, EM_AARCH64 and R_AARCH64_ABS64, to compile recordmcount utility. See [4/6]. [4/6] also gets warnings from checkpatch, but they are based on the original's coding style. * This patch may conflict with my audit patch because both changes the same location in syscall_trace(). I expect the functions are called in this order: On entry, * tracehook_report_syscall(ENTER) * trace_sys_enter() * audit_syscall_entry() On exit, * audit_sysscall_exit() * trace_sys_exit() * tracehook_report_syscall(EXIT)
Changes from v1 to v2: * splitted one patch into some pieces for easier review (especially function tracer + dynamic ftrace + CALLER_ADDRx) * put return_address() in a separate file * renamed __mcount to _mcount (it was my mistake) * changed stackframe handling to get parent's frame pointer * removed ARCH_SUPPORTS_FTRACE_OPS * switched to "hotpatch" interfaces from Huawai * revised descriptions in comments
Changes from v2 to v3: * optimized register usages in asm (by not saving x0, x1, and x2) * removed "fault protection" code in prepare_ftrace_return() * rewrote ftrace_modify_code() using "hotpatch" interfaces * revised descriptions in comments
Changes from v3 to v4: * removed unnecessary "#ifdef" [1,2/7] * changed stack depth from 48B to 16B in mcount()/ftrace_caller() (a bug) [1/7] * changed MCOUNT_INSN_SIZE to AARCH64_INSN_SIZE [1,7/7] * added a guard againt TIF_SYSCALL_TRACEPOINT [5/7] * corrected the second argument passed to trace_sys_exit() (a bug) [5/7] * aligned with the change in "arm64: make a single hook to syscall_trace() for all syscall features" v2 [5/7]
AKASHI Takahiro (7): arm64: Add ftrace support arm64: ftrace: Add dynamic ftrace support arm64: ftrace: Add CALLER_ADDRx macros ftrace: Add arm64 support to recordmcount arm64: ftrace: Add system call tracepoint arm64: Add 'notrace' attribute to unwind_frame() for ftrace arm64: add __ASSEMBLY__ in asm/insn.h
arch/arm64/Kconfig | 6 + arch/arm64/include/asm/ftrace.h | 52 +++++++++ arch/arm64/include/asm/insn.h | 2 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 + arch/arm64/kernel/Makefile | 7 +- arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 216 ++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 177 +++++++++++++++++++++++++++++ arch/arm64/kernel/ptrace.c | 9 ++ arch/arm64/kernel/return_address.c | 55 +++++++++ arch/arm64/kernel/stacktrace.c | 2 +- scripts/recordmcount.c | 4 + scripts/recordmcount.pl | 5 + 14 files changed, 540 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c create mode 100644 arch/arm64/kernel/return_address.c
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/ftrace.h | 23 +++++ arch/arm64/kernel/Makefile | 4 + arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 173 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 64 ++++++++++++++ 6 files changed, 270 insertions(+) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 27bbcfc..5783641 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -33,6 +33,8 @@ config ARM64 select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..58ea595 --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/* + * arch/arm64/include/asm/ftrace.h + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H + +#include <asm/insn.h> + +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE + +#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ac67fd0 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,9 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg + # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ @@ -13,6 +16,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o +arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..2e8162e --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,173 @@ +/* + * arch/arm64/kernel/entry-ftrace.S + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +/* + * Gcc with -pg will put the following code in the beginning of each function: + * mov x0, x30 + * bl _mcount + * [function's body ...] + * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic + * ftrace is enabled. + * + * Please note that x0 as an argument will not be used here because we can + * get lr(x30) of instrumented function at any time by winding up call stack + * as long as the kernel is compiled without -fomit-frame-pointer. + * (or CONFIG_FRAME_POINTER, this is forced on arm64) + * + * stack layout after mcount_enter: + * + * 0 ---------------------------------------- sp of _mcount() + * x29: fp of instrumented function fp is not winded + * -------------------- + * x30: lr of _mcount() (= instrumented function's pc) + * +16 ---------------------------------------- sp of instrumented function + * + * .... + * + * +xx ---------------------------------------- fp of instrumented function + * x29: fp of parent + * -------------------- + * x30: lr of instrumented function (= parent's pc) + * -------------------- + * xxx + */ + + .macro mcount_enter + stp x29, x30, [sp, #-16]! + .endm + + .macro mcount_exit + ldp x29, x30, [sp], #16 + ret + .endm + + .macro mcount_adjust_addr rd, rn + sub \rd, \rn, #AARCH64_INSN_SIZE + .endm + + /* for instrumented function's parent */ + .macro mcount_get_parent_fp reg + ldr \reg, [sp] + ldr \reg, [\reg] + .endm + + /* for instrumented function */ + .macro mcount_get_pc0 reg + mcount_adjust_addr \reg, x30 + .endm + + .macro mcount_get_pc reg + ldr \reg, [sp, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr reg + ldr \reg, [sp] + ldr \reg, [\reg, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_saved_lr_addr reg + ldr \reg, [sp] + add \reg, \reg, #8 + .endm + +/* + * void _mcount(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function makes calls, if enabled, to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST + ldr x0, =ftrace_trace_stop + ldr x0, [x0] // if ftrace_trace_stop + ret // return; +#endif + mcount_enter + + ldr x0, =ftrace_trace_function + ldr x2, [x0] + adr x0, ftrace_stub + cmp x0, x2 // if (ftrace_trace_function + b.eq skip_ftrace_call // != ftrace_stub) { + + mcount_get_pc x0 // function's pc + mcount_get_lr x1 // function's lr (= parent's pc) + blr x2 // (*ftrace_trace_function)(pc, lr); + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call: // return; + mcount_exit // } +#else + mcount_exit // return; + // } +skip_ftrace_call: + ldr x1, =ftrace_graph_return + ldr x2, [x1] // if ((ftrace_graph_return + cmp x0, x2 // != ftrace_stub) + b.ne ftrace_graph_caller + + ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry + ldr x2, [x1] // != ftrace_graph_entry_stub)) + ldr x0, =ftrace_graph_entry_stub + cmp x0, x2 + b.ne ftrace_graph_caller // ftrace_graph_caller(); + + mcount_exit +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount) + +ENTRY(ftrace_stub) + ret +ENDPROC(ftrace_stub) + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * void ftrace_graph_caller(void) + * + * Called from _mcount() or ftrace_caller() when function_graph tracer is + * selected. + * This function w/ prepare_ftrace_return() fakes link register's value on + * the call stack in order to intercept instrumented function's return path + * and run return_to_handler() later on its exit. + */ +ENTRY(ftrace_graph_caller) + mcount_get_saved_lr_addr x0 // pointer to function's saved lr + mcount_get_pc x1 // function's pc + mcount_get_parent_fp x2 // parent's fp + bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp) + + mcount_exit +ENDPROC(ftrace_graph_caller) + +/* + * void return_to_handler(void) + * + * Run ftrace_return_to_handler() before going back to parent. + * @fp is checked against the value passed by ftrace_graph_caller() + * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled. + */ + .global return_to_handler +return_to_handler: + str x0, [sp, #-16]! + mov x0, x29 // parent's fp + bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); + mov x30, x0 // restore the original return address + ldr x0, [sp], #16 + ret +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/* + * arch/arm64/kernel/ftrace.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * function_graph tracer expects ftrace_return_to_handler() to be called + * on the way back to parent. For this purpose, this function is called + * in _mcount() or ftrace_caller() to replace return address (*parent) on + * the call stack to return_to_handler. + * + * Note that @frame_pointer is used only for sanity check later. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + unsigned long frame_pointer) +{ + unsigned long return_hooker = (unsigned long)&return_to_handler; + unsigned long old; + struct ftrace_graph_ent trace; + int err; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + + /* + * Note: + * No protection against faulting at *parent, which may be seen + * on other archs. It's unlikely on AArch64. + */ + old = *parent; + *parent = return_hooker; + + trace.func = self_addr; + trace.depth = current->curr_ret_stack + 1; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + *parent = old; + return; + } + + err = ftrace_push_return_trace(old, self_addr, &trace.depth, + frame_pointer); + if (err == -EBUSY) { + *parent = old; + return; + } +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
________________________________________ From: AKASHI Takahiro takahiro.akashi@linaro.org Sent: Tuesday, February 25, 2014 2:53 PM To: rostedt@goodmis.org; fweisbec@gmail.com; mingo@redhat.com; catalin.marinas@arm.com; will.deacon@arm.com; tim.bird@sonymobile.com Cc: Kulkarni, Ganapatrao; dsaxena@linaro.org; arndb@arndb.de; linux-arm-kernel@lists.infradead.org; linaro-kernel@lists.linaro.org; linux-kernel@vger.kernel.org; AKASHI Takahiro Subject: [PATCH v4 1/7] arm64: Add ftrace support
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/ftrace.h | 23 +++++ arch/arm64/kernel/Makefile | 4 + arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 173 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 64 ++++++++++++++ 6 files changed, 270 insertions(+) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 27bbcfc..5783641 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -33,6 +33,8 @@ config ARM64 select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..58ea595 --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/* + * arch/arm64/include/asm/ftrace.h + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H + +#include <asm/insn.h> + +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE + +#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ac67fd0 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,9 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg + # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ @@ -13,6 +16,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o +arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..2e8162e --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,173 @@ +/* + * arch/arm64/kernel/entry-ftrace.S + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +/* + * Gcc with -pg will put the following code in the beginning of each function: + * mov x0, x30 + * bl _mcount + * [function's body ...] + * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic + * ftrace is enabled. + * + * Please note that x0 as an argument will not be used here because we can + * get lr(x30) of instrumented function at any time by winding up call stack + * as long as the kernel is compiled without -fomit-frame-pointer. + * (or CONFIG_FRAME_POINTER, this is forced on arm64) + * + * stack layout after mcount_enter: + * + * 0 ---------------------------------------- sp of _mcount() + * x29: fp of instrumented function fp is not winded + * -------------------- + * x30: lr of _mcount() (= instrumented function's pc) + * +16 ---------------------------------------- sp of instrumented function + * + * .... + * + * +xx ---------------------------------------- fp of instrumented function + * x29: fp of parent + * -------------------- + * x30: lr of instrumented function (= parent's pc) + * -------------------- + * xxx + */ + can stack layout be more readable as below?
Stack layout of _mcount after mcount_enter
|------------------| sp + 16 <= frame pointer of _mcount. | | | x30 | <= lr of _mcount (instrumented func's pc) | | |------------------| sp + 8 | | | x29 | <= value of x29 is sp of instrumented function. | | |------------------| sp <= stack pointer of _mcount
Stack layout of instrumented function. . . . . |------------------| sp + 16 | | | x30 | <= lr of instrumented function (= parent's pc) | | |------------------| sp + 8 | | | x29 | <= sp of parent. | | |------------------| sp <= this address is stored at sp of _mcount
+ .macro mcount_enter + stp x29, x30, [sp, #-16]! + .endm + + .macro mcount_exit + ldp x29, x30, [sp], #16 + ret + .endm + + .macro mcount_adjust_addr rd, rn + sub \rd, \rn, #AARCH64_INSN_SIZE + .endm + + /* for instrumented function's parent */ + .macro mcount_get_parent_fp reg + ldr \reg, [sp] + ldr \reg, [\reg] + .endm + + /* for instrumented function */ + .macro mcount_get_pc0 reg + mcount_adjust_addr \reg, x30 + .endm + + .macro mcount_get_pc reg + ldr \reg, [sp, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr reg + ldr \reg, [sp] + ldr \reg, [\reg, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_saved_lr_addr reg + ldr \reg, [sp] + add \reg, \reg, #8 + .endm + +/* + * void _mcount(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function makes calls, if enabled, to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST + ldr x0, =ftrace_trace_stop + ldr x0, [x0] // if ftrace_trace_stop + ret // return; +#endif + mcount_enter + + ldr x0, =ftrace_trace_function + ldr x2, [x0] + adr x0, ftrace_stub + cmp x0, x2 // if (ftrace_trace_function + b.eq skip_ftrace_call // != ftrace_stub) { + + mcount_get_pc x0 // function's pc + mcount_get_lr x1 // function's lr (= parent's pc) + blr x2 // (*ftrace_trace_function)(pc, lr); + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call: // return; + mcount_exit // } +#else + mcount_exit // return; + // } +skip_ftrace_call: + ldr x1, =ftrace_graph_return + ldr x2, [x1] // if ((ftrace_graph_return + cmp x0, x2 // != ftrace_stub) + b.ne ftrace_graph_caller + + ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry + ldr x2, [x1] // != ftrace_graph_entry_stub)) + ldr x0, =ftrace_graph_entry_stub + cmp x0, x2 + b.ne ftrace_graph_caller // ftrace_graph_caller(); + + mcount_exit +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount) + +ENTRY(ftrace_stub) + ret +ENDPROC(ftrace_stub) + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * void ftrace_graph_caller(void) + * + * Called from _mcount() or ftrace_caller() when function_graph tracer is + * selected. + * This function w/ prepare_ftrace_return() fakes link register's value on + * the call stack in order to intercept instrumented function's return path + * and run return_to_handler() later on its exit. + */ +ENTRY(ftrace_graph_caller) + mcount_get_saved_lr_addr x0 // pointer to function's saved lr + mcount_get_pc x1 // function's pc + mcount_get_parent_fp x2 // parent's fp + bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp) + + mcount_exit +ENDPROC(ftrace_graph_caller) + +/* + * void return_to_handler(void) + * + * Run ftrace_return_to_handler() before going back to parent. + * @fp is checked against the value passed by ftrace_graph_caller() + * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled. + */ + .global return_to_handler +return_to_handler: + str x0, [sp, #-16]! + mov x0, x29 // parent's fp + bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); + mov x30, x0 // restore the original return address + ldr x0, [sp], #16 + ret +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/* + * arch/arm64/kernel/ftrace.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * function_graph tracer expects ftrace_return_to_handler() to be called + * on the way back to parent. For this purpose, this function is called + * in _mcount() or ftrace_caller() to replace return address (*parent) on + * the call stack to return_to_handler. + * + * Note that @frame_pointer is used only for sanity check later. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + unsigned long frame_pointer) +{ + unsigned long return_hooker = (unsigned long)&return_to_handler; + unsigned long old; + struct ftrace_graph_ent trace; + int err; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + + /* + * Note: + * No protection against faulting at *parent, which may be seen + * on other archs. It's unlikely on AArch64. + */ + old = *parent; + *parent = return_hooker; + + trace.func = self_addr; + trace.depth = current->curr_ret_stack + 1; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + *parent = old; + return; + } + + err = ftrace_push_return_trace(old, self_addr, &trace.depth, + frame_pointer); + if (err == -EBUSY) { + *parent = old; + return; + } +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ -- 1.7.9.5
Ganapatrao,
On 02/26/2014 12:38 AM, Kulkarni, Ganapatrao wrote:
From: AKASHI Takahiro takahiro.akashi@linaro.org Sent: Tuesday, February 25, 2014 2:53 PM To: rostedt@goodmis.org; fweisbec@gmail.com; mingo@redhat.com; catalin.marinas@arm.com; will.deacon@arm.com; tim.bird@sonymobile.com Cc: Kulkarni, Ganapatrao; dsaxena@linaro.org; arndb@arndb.de; linux-arm-kernel@lists.infradead.org; linaro-kernel@lists.linaro.org; linux-kernel@vger.kernel.org; AKASHI Takahiro Subject: [PATCH v4 1/7] arm64: Add ftrace support
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/Kconfig | 2 + arch/arm64/include/asm/ftrace.h | 23 +++++ arch/arm64/kernel/Makefile | 4 + arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 173 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 64 ++++++++++++++ 6 files changed, 270 insertions(+) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 27bbcfc..5783641 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -33,6 +33,8 @@ config ARM64 select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..58ea595 --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/*
- arch/arm64/include/asm/ftrace.h
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H
+#include <asm/insn.h>
+#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE
+#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */
+#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ac67fd0 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,9 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg
- # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \
@@ -13,6 +16,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o +arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit);
+#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..2e8162e --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,173 @@ +/*
- arch/arm64/kernel/entry-ftrace.S
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h>
+/*
- Gcc with -pg will put the following code in the beginning of each function:
mov x0, x30
bl _mcount
[function's body ...]
- "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic
- ftrace is enabled.
- Please note that x0 as an argument will not be used here because we can
- get lr(x30) of instrumented function at any time by winding up call stack
- as long as the kernel is compiled without -fomit-frame-pointer.
- (or CONFIG_FRAME_POINTER, this is forced on arm64)
- stack layout after mcount_enter:
- 0 ---------------------------------------- sp of _mcount()
x29: fp of instrumented function fp is not winded
--------------------
x30: lr of _mcount() (= instrumented function's pc)
- +16 ---------------------------------------- sp of instrumented function
....
- +xx ---------------------------------------- fp of instrumented function
x29: fp of parent
--------------------
x30: lr of instrumented function (= parent's pc)
--------------------
xxx
- */
can stack layout be more readable as below?
Stack layout of _mcount after mcount_enter
|------------------| sp + 16 <= frame pointer of _mcount. | | | x30 | <= lr of _mcount (instrumented func's pc) | | |------------------| sp + 8 | | | x29 | <= value of x29 is sp of instrumented function.
not sp, but fp. Anyway pls check with my new patch v5.
Thanks, -Takahiro AKASHI
| | |------------------| sp <= stack pointer of _mcount
Stack layout of instrumented function. . . . . |------------------| sp + 16 | | | x30 | <= lr of instrumented function (= parent's pc) | | |------------------| sp + 8 | | | x29 | <= sp of parent. | | |------------------| sp <= this address is stored at sp of _mcount
.macro mcount_enter
stp x29, x30, [sp, #-16]!
.endm
.macro mcount_exit
ldp x29, x30, [sp], #16
ret
.endm
.macro mcount_adjust_addr rd, rn
sub \rd, \rn, #AARCH64_INSN_SIZE
.endm
/* for instrumented function's parent */
.macro mcount_get_parent_fp reg
ldr \reg, [sp]
ldr \reg, [\reg]
.endm
/* for instrumented function */
.macro mcount_get_pc0 reg
mcount_adjust_addr \reg, x30
.endm
.macro mcount_get_pc reg
ldr \reg, [sp, #8]
mcount_adjust_addr \reg, \reg
.endm
.macro mcount_get_lr reg
ldr \reg, [sp]
ldr \reg, [\reg, #8]
mcount_adjust_addr \reg, \reg
.endm
.macro mcount_get_saved_lr_addr reg
ldr \reg, [sp]
add \reg, \reg, #8
.endm
+/*
- void _mcount(unsigned long return_address)
- @return_address: return address to instrumented function
- This function makes calls, if enabled, to:
- tracer function to probe instrumented function's entry,
- ftrace_graph_caller to set up an exit hook
- */
+ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
ldr x0, =ftrace_trace_stop
ldr x0, [x0] // if ftrace_trace_stop
ret // return;
+#endif
mcount_enter
ldr x0, =ftrace_trace_function
ldr x2, [x0]
adr x0, ftrace_stub
cmp x0, x2 // if (ftrace_trace_function
b.eq skip_ftrace_call // != ftrace_stub) {
mcount_get_pc x0 // function's pc
mcount_get_lr x1 // function's lr (= parent's pc)
blr x2 // (*ftrace_trace_function)(pc, lr);
+#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call: // return;
mcount_exit // }
+#else
mcount_exit // return;
// }
+skip_ftrace_call:
ldr x1, =ftrace_graph_return
ldr x2, [x1] // if ((ftrace_graph_return
cmp x0, x2 // != ftrace_stub)
b.ne ftrace_graph_caller
ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry
ldr x2, [x1] // != ftrace_graph_entry_stub))
ldr x0, =ftrace_graph_entry_stub
cmp x0, x2
b.ne ftrace_graph_caller // ftrace_graph_caller();
mcount_exit
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount)
+ENTRY(ftrace_stub)
ret
+ENDPROC(ftrace_stub)
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/*
- void ftrace_graph_caller(void)
- Called from _mcount() or ftrace_caller() when function_graph tracer is
- selected.
- This function w/ prepare_ftrace_return() fakes link register's value on
- the call stack in order to intercept instrumented function's return path
- and run return_to_handler() later on its exit.
- */
+ENTRY(ftrace_graph_caller)
mcount_get_saved_lr_addr x0 // pointer to function's saved lr
mcount_get_pc x1 // function's pc
mcount_get_parent_fp x2 // parent's fp
bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp)
mcount_exit
+ENDPROC(ftrace_graph_caller)
+/*
- void return_to_handler(void)
- Run ftrace_return_to_handler() before going back to parent.
- @fp is checked against the value passed by ftrace_graph_caller()
- only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled.
- */
.global return_to_handler
+return_to_handler:
str x0, [sp, #-16]!
mov x0, x29 // parent's fp
bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp);
mov x30, x0 // restore the original return address
ldr x0, [sp], #16
ret
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/*
- arch/arm64/kernel/ftrace.c
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h>
+#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h>
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/*
- function_graph tracer expects ftrace_return_to_handler() to be called
- on the way back to parent. For this purpose, this function is called
- in _mcount() or ftrace_caller() to replace return address (*parent) on
- the call stack to return_to_handler.
- Note that @frame_pointer is used only for sanity check later.
- */
+void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
unsigned long frame_pointer)
+{
unsigned long return_hooker = (unsigned long)&return_to_handler;
unsigned long old;
struct ftrace_graph_ent trace;
int err;
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
return;
/*
* Note:
* No protection against faulting at *parent, which may be seen
* on other archs. It's unlikely on AArch64.
*/
old = *parent;
*parent = return_hooker;
trace.func = self_addr;
trace.depth = current->curr_ret_stack + 1;
/* Only trace if the calling function expects to */
if (!ftrace_graph_entry(&trace)) {
*parent = old;
return;
}
err = ftrace_push_return_trace(old, self_addr, &trace.depth,
frame_pointer);
if (err == -EBUSY) {
*parent = old;
return;
}
+}
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
1.7.9.5
This patch allows "dynamic ftrace" if CONFIG_DYNAMIC_FTRACE is enabled. Here we can turn on and off tracing dynamically per-function base.
On arm64, this is done by patching single branch instruction to _mcount() inserted by gcc -pg option. The branch is replaced to NOP initially at kernel start up, and later on, NOP to branch to ftrace_caller() when enabled or branch to NOP when disabled. Please note that ftrace_caller() is a counterpart of _mcount() in case of 'static' ftrace.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 15 +++++ arch/arm64/kernel/entry-ftrace.S | 43 +++++++++++++++ arch/arm64/kernel/ftrace.c | 113 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 5783641..b176bc0 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -32,6 +32,7 @@ config ARM64 select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS + select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 58ea595..ed5c448 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,21 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); + +struct dyn_arch_ftrace { + /* No extra data needed for arm64 */ +}; + +extern unsigned long ftrace_graph_call; + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ + /* + * addr is the address of the mcount call instruction. + * recordmcount does the necessary offset calculation. + */ + return addr; +} #endif /* __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S index 2e8162e..b948878 100644 --- a/arch/arm64/kernel/entry-ftrace.S +++ b/arch/arm64/kernel/entry-ftrace.S @@ -84,6 +84,7 @@ add \reg, \reg, #8 .endm
+#ifndef CONFIG_DYNAMIC_FTRACE /* * void _mcount(unsigned long return_address) * @return_address: return address to instrumented function @@ -132,6 +133,48 @@ skip_ftrace_call: #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ENDPROC(_mcount)
+#else /* CONFIG_DYNAMIC_FTRACE */ +/* + * _mcount() is used to build the kernel with -pg option, but all the branch + * instructions to _mcount() are replaced to NOP initially at kernel start up, + * and later on, NOP to branch to ftrace_caller() when enabled or branch to + * NOP when disabled per-function base. + */ +ENTRY(_mcount) + ret +ENDPROC(_mcount) + +/* + * void ftrace_caller(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function is a counterpart of _mcount() in 'static' ftrace, and + * makes calls to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(ftrace_caller) + mcount_enter + + mcount_get_pc0 x0 // function's pc + mcount_get_lr x1 // function's lr + + .global ftrace_call +ftrace_call: // tracer(pc, lr); + nop // This will be replaced with "bl xxx" + // where xxx can be any kind of tracer. + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + .global ftrace_graph_call +ftrace_graph_call: // ftrace_graph_caller(); + nop // If enabled, this will be replaced + // "b ftrace_graph_caller" +#endif + + mcount_exit +ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */ + ENTRY(ftrace_stub) ret ENDPROC(ftrace_stub) diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index a559ab8..8c26476 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -17,6 +17,89 @@ #include <asm/ftrace.h> #include <asm/insn.h>
+#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Replace a single instruction, which may be a branch or NOP. + * If @validate == true, a replaced instruction is checked against 'old'. + */ +static int ftrace_modify_code(unsigned long pc, unsigned int old, + unsigned int new, bool validate) +{ + unsigned int replaced; + + /* + * Note: + * Due to modules and __init, code can disappear and change, + * we need to protect against faulting as well as code changing. + * We do this by aarch64_insn_*() which use the probe_kernel_*(). + * + * No lock is held here because all the modifications are run + * through stop_machine(). + */ + if (validate) { + if (aarch64_insn_read((void *)pc, &replaced)) + return -EFAULT; + + if (replaced != old) + return -EINVAL; + } + if (aarch64_insn_patch_text_nosync((void *)pc, new)) + return -EPERM; + + return 0; +} + +/* + * Replace tracer function in ftrace_caller() + */ +int ftrace_update_ftrace_func(ftrace_func_t func) +{ + unsigned long pc; + unsigned int new; + + pc = (unsigned long)&ftrace_call; + new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); + + return ftrace_modify_code(pc, 0, new, false); +} + +/* + * Turn on the call to ftrace_caller() in instrumented function + */ +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_nop(); + new = aarch64_insn_gen_branch_imm(pc, addr, true); + + return ftrace_modify_code(pc, old, new, true); +} + +/* + * Turn off the call to ftrace_caller() in instrumented function + */ +int ftrace_make_nop(struct module *mod, + struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_branch_imm(pc, addr, true); + new = aarch64_insn_gen_nop(); + + return ftrace_modify_code(pc, old, new, true); +} + +int __init ftrace_dyn_arch_init(void *data) +{ + *(unsigned long *)data = 0; + + return 0; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* * function_graph tracer expects ftrace_return_to_handler() to be called @@ -61,4 +144,34 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } } + +#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Turn on/off the call to ftrace_graph_caller() in ftrace_caller() + * depending on @enable. + */ +static int ftrace_modify_graph_caller(bool enable) +{ + unsigned long pc = (unsigned long)&ftrace_graph_call; + unsigned int branch, nop, old, new; + + branch = aarch64_insn_gen_branch_imm(pc, + (unsigned long)ftrace_graph_caller, false); + nop = aarch64_insn_gen_nop(); + old = enable ? nop : branch; + new = enable ? branch : nop; + + return ftrace_modify_code(pc, old, new, true); +} + +int ftrace_enable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(true); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(false); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/ftrace.h | 14 +++++++++ arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..f93da72 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -35,4 +35,18 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) } #endif /* __ASSEMBLY__ */
+#ifndef __ASSEMBLY__ +#define HAVE_ARCH_CALLER_ADDR + +extern void *return_address(unsigned int); + +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#endif /* ifndef __ASSEMBLY__ */ + #endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index ac67fd0..b5bfa7f 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -7,12 +7,13 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_insn.o = -pg +CFLAGS_REMOVE_return_address.o = -pg
# Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ - hyp-stub.o psci.o cpu_ops.o insn.o + hyp-stub.o psci.o cpu_ops.o insn.o return_address.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 0000000..89102a6 --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/* + * arch/arm64/kernel/return_address.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/export.h> +#include <linux/ftrace.h> + +#include <asm/stacktrace.h> + +struct return_address_data { + unsigned int level; + void *addr; +}; + +static int save_return_addr(struct stackframe *frame, void *d) +{ + struct return_address_data *data = d; + + if (!data->level) { + data->addr = (void *)frame->pc; + return 1; + } else { + --data->level; + return 0; + } +} + +void *return_address(unsigned int level) +{ + struct return_address_data data; + struct stackframe frame; + register unsigned long current_sp asm ("sp"); + + data.level = level + 2; + data.addr = NULL; + + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.pc = (unsigned long)return_address; /* dummy */ + + walk_stackframe(&frame, save_return_addr, &data); + + if (!data.level) + return data.addr; + else + return NULL; +} +EXPORT_SYMBOL_GPL(return_address);
Recordmcount utility under scripts is run, after compiling each object, to find out all the locations of calling _mcount() and put them into specific seciton named __mcount_loc. Then linker collects all such information into a table in the kernel image (between __start_mcount_loc and __stop_mcount_loc) for later use by ftrace.
This patch adds arm64 specific definitions to identify such locations. There are two types of implementation, C and Perl. On arm64, only C version is used to build the kernel now that CONFIG_HAVE_C_RECORDMCOUNT is on. But Perl version is also maintained.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 ++ scripts/recordmcount.c | 4 ++++ scripts/recordmcount.pl | 5 +++++ 3 files changed, 11 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b176bc0..6954959 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -27,6 +27,7 @@ config ARM64 select HARDIRQS_SW_RESEND select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_TRACEHOOK + select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG @@ -34,6 +35,7 @@ config ARM64 select HAVE_DMA_CONTIGUOUS select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 9c22317..b92d9f7 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -347,6 +347,10 @@ do_file(char const *const fname) case EM_ARM: reltype = R_ARM_ABS32; altmcount = "__gnu_mcount_nc"; break; + case EM_AARCH64: + reltype = R_AARCH64_ABS64; + altmcount = "_mcount"; + break; case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; case EM_METAG: reltype = R_METAG_ADDR32; altmcount = "_mcount_wrapper"; diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 91280b8..397b6b8 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$";
+} elsif ($arch eq "arm64") { + $alignment = 3; + $section_type = '%progbits'; + $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$"; + $type = ".quad"; } elsif ($arch eq "ia64") { $mcount_regex = "^\s*([0-9a-fA-F]+):.*\s_mcount$"; $type = "data8";
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 9 +++++++++ 4 files changed, 13 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6954959..b1dcdb4 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -43,6 +43,7 @@ config ARM64 select HAVE_MEMBLOCK select HAVE_PATA_PLATFORM select HAVE_PERF_EVENTS + select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@
#include <linux/err.h>
+extern const void *sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h> + +#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index c70133e..dc90f9f 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -41,6 +41,9 @@ #include <asm/traps.h> #include <asm/system_misc.h>
+#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> + /* * TODO: does not yet catch signals sent when the child dies. * in exit.c or in signal.c. @@ -1062,6 +1065,9 @@ asmlinkage int syscall_trace(int dir, struct pt_regs *regs) { unsigned long saved_reg;
+ if (dir && unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) + trace_sys_exit(regs, regs_return_value(regs)); + if (test_thread_flag(TIF_SYSCALL_TRACE)) { if (is_compat_task()) { /* AArch32 uses ip (r12) for scratch */ @@ -1087,5 +1093,8 @@ asmlinkage int syscall_trace(int dir, struct pt_regs *regs) regs->regs[7] = saved_reg; }
+ if (!dir && unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) + trace_sys_enter(regs, regs->syscallno); + return regs->syscallno; }
walk_stackframe() calls unwind_frame(), and if walk_stackframe() is "notrace", unwind_frame() should be also "notrace".
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index c3b6c63..54122c4 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -35,7 +35,7 @@ * ldp x29, x30, [sp] * add sp, sp, #0x10 */ -int unwind_frame(struct stackframe *frame) +int notrace unwind_frame(struct stackframe *frame) { unsigned long high, low; unsigned long fp = frame->fp;
Since insn.h is indirectly included in asm/entry-ftrace.S, we need to exclude some declarations by __ASSEMBLY__.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/insn.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index c44ad39..dc1f73b 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -21,6 +21,7 @@ /* A64 instructions are always 32 bits. */ #define AARCH64_INSN_SIZE 4
+#ifndef __ASSEMBLY__ /* * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a * Section C3.1 "A64 instruction index by encoding": @@ -104,5 +105,6 @@ bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); int aarch64_insn_patch_text_nosync(void *addr, u32 insn); int aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt); int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt); +#endif /* __ASSEMBLY__ */
#endif /* __ASM_INSN_H */
This patchset implements a function tracer on arm64. There was another implementation from Cavium network, but both of us agreed to use my patchset as future base. He is supposed to review this code, too.
The only issue that I had some concern on was "fault protection" code in prepare_ftrace_return(). With discussions with Steven and Tim (as author of arm ftrace), I removed that code since I'm not quite sure about possibility of "fault" occurrences in this function.
The code is tested on ARMv8 Fast Model with the following tracers & events: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint irqsoff & preemptirqsoff (which use CALLER_ADDRx) and also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Prerequisites are: * "arm64: Add regs_return_value() in syscall.h" * "arm64: make a single hook to syscall_trace() for all syscall features" patch
Please be careful: * elf.h on cross-build host must have AArch64 definitions, EM_AARCH64 and R_AARCH64_ABS64, to compile recordmcount utility. See [4/6]. [4/6] also gets warnings from checkpatch, but they are based on the original's coding style. * This patch may conflict with my audit patch because both changes the same location in syscall_trace(). I expect the functions are called in this order: On entry, * tracehook_report_syscall(ENTER) * trace_sys_enter() * audit_syscall_entry() On exit, * audit_sysscall_exit() * trace_sys_exit() * tracehook_report_syscall(EXIT)
Changes from v1 to v2: * splitted one patch into some pieces for easier review (especially function tracer + dynamic ftrace + CALLER_ADDRx) * put return_address() in a separate file * renamed __mcount to _mcount (it was my mistake) * changed stackframe handling to get parent's frame pointer * removed ARCH_SUPPORTS_FTRACE_OPS * switched to "hotpatch" interfaces from Huawai * revised descriptions in comments
Changes from v2 to v3: * optimized register usages in asm (by not saving x0, x1, and x2) * removed "fault protection" code in prepare_ftrace_return() * rewrote ftrace_modify_code() using "hotpatch" interfaces * revised descriptions in comments
Changes from v3 to v4: * removed unnecessary "#ifdef" [1,2/7] * changed stack depth from 48B to 16B in mcount()/ftrace_caller() (a bug) [1/7] * changed MCOUNT_INSN_SIZE to AARCH64_INSN_SIZE [1,7/7] * added a guard againt TIF_SYSCALL_TRACEPOINT [5/7] * corrected the second argument passed to trace_sys_exit() (a bug) [5/7] * aligned with the change in "arm64: make a single hook to syscall_trace() for all syscall features" v2 [5/7]
Chnages from v4 to v5: * improved the description of stack layout [1/7] * aligned with the change in "arm64: make a single hook to syscall_trace() for all syscall features" v3 [5/7]
AKASHI Takahiro (7): arm64: Add ftrace support arm64: ftrace: Add dynamic ftrace support arm64: ftrace: Add CALLER_ADDRx macros ftrace: Add arm64 support to recordmcount arm64: ftrace: Add system call tracepoint arm64: Add 'notrace' attribute to unwind_frame() for ftrace arm64: add __ASSEMBLY__ in asm/insn.h
arch/arm64/Kconfig | 6 + arch/arm64/include/asm/ftrace.h | 52 +++++++++ arch/arm64/include/asm/insn.h | 2 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 + arch/arm64/kernel/Makefile | 7 +- arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 217 ++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 177 +++++++++++++++++++++++++++++ arch/arm64/kernel/ptrace.c | 52 +++++---- arch/arm64/kernel/return_address.c | 55 +++++++++ arch/arm64/kernel/stacktrace.c | 2 +- scripts/recordmcount.c | 4 + scripts/recordmcount.pl | 5 + 14 files changed, 562 insertions(+), 24 deletions(-) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c create mode 100644 arch/arm64/kernel/return_address.c
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Reviewed-by: Ganapatrao Kulkarni ganapatrao.kulkarni@cavium.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/ftrace.h | 23 +++++ arch/arm64/kernel/Makefile | 4 + arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 174 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 64 ++++++++++++++ 6 files changed, 271 insertions(+) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 27bbcfc..5783641 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -33,6 +33,8 @@ config ARM64 select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..58ea595 --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/* + * arch/arm64/include/asm/ftrace.h + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H + +#include <asm/insn.h> + +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE + +#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ac67fd0 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,9 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg + # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ @@ -13,6 +16,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o +arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..0e5b8b0 --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,174 @@ +/* + * arch/arm64/kernel/entry-ftrace.S + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +/* + * Gcc with -pg will put the following code in the beginning of each function: + * mov x0, x30 + * bl _mcount + * [function's body ...] + * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic + * ftrace is enabled. + * + * Please note that x0 as an argument will not be used here because we can + * get lr(x30) of instrumented function at any time by winding up call stack + * as long as the kernel is compiled without -fomit-frame-pointer. + * (or CONFIG_FRAME_POINTER, this is forced on arm64) + * + * stack layout after mcount_enter in _mcount(): + * + * current sp => 0:+-----+ + * | x29 | -> instrumented function's fp + * +-----+ + * | x30 | -> _mcount()'s lr (= instrumented function's pc) + * old sp => +16:+-----+ + * when instrumented | | + * function calls | ... | + * _mcount() | | + * | | + * instrumented => +xx:+-----+ + * function's fp | x29 | -> parent's fp + * +-----+ + * | x30 | -> instrumented function's lr (= parent's pc) + * +-----+ + * | ... | + */ + + .macro mcount_enter + stp x29, x30, [sp, #-16]! + .endm + + .macro mcount_exit + ldp x29, x30, [sp], #16 + ret + .endm + + .macro mcount_adjust_addr rd, rn + sub \rd, \rn, #AARCH64_INSN_SIZE + .endm + + /* for instrumented function's parent */ + .macro mcount_get_parent_fp reg + ldr \reg, [sp] + ldr \reg, [\reg] + .endm + + /* for instrumented function */ + .macro mcount_get_pc0 reg + mcount_adjust_addr \reg, x30 + .endm + + .macro mcount_get_pc reg + ldr \reg, [sp, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr reg + ldr \reg, [sp] + ldr \reg, [\reg, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr_addr reg + ldr \reg, [sp] + add \reg, \reg, #8 + .endm + +/* + * void _mcount(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function makes calls, if enabled, to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST + ldr x0, =ftrace_trace_stop + ldr x0, [x0] // if ftrace_trace_stop + ret // return; +#endif + mcount_enter + + ldr x0, =ftrace_trace_function + ldr x2, [x0] + adr x0, ftrace_stub + cmp x0, x2 // if (ftrace_trace_function + b.eq skip_ftrace_call // != ftrace_stub) { + + mcount_get_pc x0 // function's pc + mcount_get_lr x1 // function's lr (= parent's pc) + blr x2 // (*ftrace_trace_function)(pc, lr); + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call: // return; + mcount_exit // } +#else + mcount_exit // return; + // } +skip_ftrace_call: + ldr x1, =ftrace_graph_return + ldr x2, [x1] // if ((ftrace_graph_return + cmp x0, x2 // != ftrace_stub) + b.ne ftrace_graph_caller + + ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry + ldr x2, [x1] // != ftrace_graph_entry_stub)) + ldr x0, =ftrace_graph_entry_stub + cmp x0, x2 + b.ne ftrace_graph_caller // ftrace_graph_caller(); + + mcount_exit +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount) + +ENTRY(ftrace_stub) + ret +ENDPROC(ftrace_stub) + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * void ftrace_graph_caller(void) + * + * Called from _mcount() or ftrace_caller() when function_graph tracer is + * selected. + * This function w/ prepare_ftrace_return() fakes link register's value on + * the call stack in order to intercept instrumented function's return path + * and run return_to_handler() later on its exit. + */ +ENTRY(ftrace_graph_caller) + mcount_get_lr_addr x0 // pointer to function's saved lr + mcount_get_pc x1 // function's pc + mcount_get_parent_fp x2 // parent's fp + bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp) + + mcount_exit +ENDPROC(ftrace_graph_caller) + +/* + * void return_to_handler(void) + * + * Run ftrace_return_to_handler() before going back to parent. + * @fp is checked against the value passed by ftrace_graph_caller() + * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled. + */ + .global return_to_handler +return_to_handler: + str x0, [sp, #-16]! + mov x0, x29 // parent's fp + bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); + mov x30, x0 // restore the original return address + ldr x0, [sp], #16 + ret +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/* + * arch/arm64/kernel/ftrace.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * function_graph tracer expects ftrace_return_to_handler() to be called + * on the way back to parent. For this purpose, this function is called + * in _mcount() or ftrace_caller() to replace return address (*parent) on + * the call stack to return_to_handler. + * + * Note that @frame_pointer is used only for sanity check later. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + unsigned long frame_pointer) +{ + unsigned long return_hooker = (unsigned long)&return_to_handler; + unsigned long old; + struct ftrace_graph_ent trace; + int err; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + + /* + * Note: + * No protection against faulting at *parent, which may be seen + * on other archs. It's unlikely on AArch64. + */ + old = *parent; + *parent = return_hooker; + + trace.func = self_addr; + trace.depth = current->curr_ret_stack + 1; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + *parent = old; + return; + } + + err = ftrace_push_return_trace(old, self_addr, &trace.depth, + frame_pointer); + if (err == -EBUSY) { + *parent = old; + return; + } +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
This patch allows "dynamic ftrace" if CONFIG_DYNAMIC_FTRACE is enabled. Here we can turn on and off tracing dynamically per-function base.
On arm64, this is done by patching single branch instruction to _mcount() inserted by gcc -pg option. The branch is replaced to NOP initially at kernel start up, and later on, NOP to branch to ftrace_caller() when enabled or branch to NOP when disabled. Please note that ftrace_caller() is a counterpart of _mcount() in case of 'static' ftrace.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 15 +++++ arch/arm64/kernel/entry-ftrace.S | 43 +++++++++++++++ arch/arm64/kernel/ftrace.c | 113 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 5783641..b176bc0 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -32,6 +32,7 @@ config ARM64 select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS + select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 58ea595..ed5c448 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,21 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); + +struct dyn_arch_ftrace { + /* No extra data needed for arm64 */ +}; + +extern unsigned long ftrace_graph_call; + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ + /* + * addr is the address of the mcount call instruction. + * recordmcount does the necessary offset calculation. + */ + return addr; +} #endif /* __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S index 0e5b8b0..c012cf0 100644 --- a/arch/arm64/kernel/entry-ftrace.S +++ b/arch/arm64/kernel/entry-ftrace.S @@ -85,6 +85,7 @@ add \reg, \reg, #8 .endm
+#ifndef CONFIG_DYNAMIC_FTRACE /* * void _mcount(unsigned long return_address) * @return_address: return address to instrumented function @@ -133,6 +134,48 @@ skip_ftrace_call: #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ENDPROC(_mcount)
+#else /* CONFIG_DYNAMIC_FTRACE */ +/* + * _mcount() is used to build the kernel with -pg option, but all the branch + * instructions to _mcount() are replaced to NOP initially at kernel start up, + * and later on, NOP to branch to ftrace_caller() when enabled or branch to + * NOP when disabled per-function base. + */ +ENTRY(_mcount) + ret +ENDPROC(_mcount) + +/* + * void ftrace_caller(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function is a counterpart of _mcount() in 'static' ftrace, and + * makes calls to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(ftrace_caller) + mcount_enter + + mcount_get_pc0 x0 // function's pc + mcount_get_lr x1 // function's lr + + .global ftrace_call +ftrace_call: // tracer(pc, lr); + nop // This will be replaced with "bl xxx" + // where xxx can be any kind of tracer. + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + .global ftrace_graph_call +ftrace_graph_call: // ftrace_graph_caller(); + nop // If enabled, this will be replaced + // "b ftrace_graph_caller" +#endif + + mcount_exit +ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */ + ENTRY(ftrace_stub) ret ENDPROC(ftrace_stub) diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index a559ab8..8c26476 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -17,6 +17,89 @@ #include <asm/ftrace.h> #include <asm/insn.h>
+#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Replace a single instruction, which may be a branch or NOP. + * If @validate == true, a replaced instruction is checked against 'old'. + */ +static int ftrace_modify_code(unsigned long pc, unsigned int old, + unsigned int new, bool validate) +{ + unsigned int replaced; + + /* + * Note: + * Due to modules and __init, code can disappear and change, + * we need to protect against faulting as well as code changing. + * We do this by aarch64_insn_*() which use the probe_kernel_*(). + * + * No lock is held here because all the modifications are run + * through stop_machine(). + */ + if (validate) { + if (aarch64_insn_read((void *)pc, &replaced)) + return -EFAULT; + + if (replaced != old) + return -EINVAL; + } + if (aarch64_insn_patch_text_nosync((void *)pc, new)) + return -EPERM; + + return 0; +} + +/* + * Replace tracer function in ftrace_caller() + */ +int ftrace_update_ftrace_func(ftrace_func_t func) +{ + unsigned long pc; + unsigned int new; + + pc = (unsigned long)&ftrace_call; + new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); + + return ftrace_modify_code(pc, 0, new, false); +} + +/* + * Turn on the call to ftrace_caller() in instrumented function + */ +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_nop(); + new = aarch64_insn_gen_branch_imm(pc, addr, true); + + return ftrace_modify_code(pc, old, new, true); +} + +/* + * Turn off the call to ftrace_caller() in instrumented function + */ +int ftrace_make_nop(struct module *mod, + struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_branch_imm(pc, addr, true); + new = aarch64_insn_gen_nop(); + + return ftrace_modify_code(pc, old, new, true); +} + +int __init ftrace_dyn_arch_init(void *data) +{ + *(unsigned long *)data = 0; + + return 0; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* * function_graph tracer expects ftrace_return_to_handler() to be called @@ -61,4 +144,34 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } } + +#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Turn on/off the call to ftrace_graph_caller() in ftrace_caller() + * depending on @enable. + */ +static int ftrace_modify_graph_caller(bool enable) +{ + unsigned long pc = (unsigned long)&ftrace_graph_call; + unsigned int branch, nop, old, new; + + branch = aarch64_insn_gen_branch_imm(pc, + (unsigned long)ftrace_graph_caller, false); + nop = aarch64_insn_gen_nop(); + old = enable ? nop : branch; + new = enable ? branch : nop; + + return ftrace_modify_code(pc, old, new, true); +} + +int ftrace_enable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(true); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(false); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/ftrace.h | 14 +++++++++ arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..f93da72 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -35,4 +35,18 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) } #endif /* __ASSEMBLY__ */
+#ifndef __ASSEMBLY__ +#define HAVE_ARCH_CALLER_ADDR + +extern void *return_address(unsigned int); + +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#endif /* ifndef __ASSEMBLY__ */ + #endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index ac67fd0..b5bfa7f 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -7,12 +7,13 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_insn.o = -pg +CFLAGS_REMOVE_return_address.o = -pg
# Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ - hyp-stub.o psci.o cpu_ops.o insn.o + hyp-stub.o psci.o cpu_ops.o insn.o return_address.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 0000000..89102a6 --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/* + * arch/arm64/kernel/return_address.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/export.h> +#include <linux/ftrace.h> + +#include <asm/stacktrace.h> + +struct return_address_data { + unsigned int level; + void *addr; +}; + +static int save_return_addr(struct stackframe *frame, void *d) +{ + struct return_address_data *data = d; + + if (!data->level) { + data->addr = (void *)frame->pc; + return 1; + } else { + --data->level; + return 0; + } +} + +void *return_address(unsigned int level) +{ + struct return_address_data data; + struct stackframe frame; + register unsigned long current_sp asm ("sp"); + + data.level = level + 2; + data.addr = NULL; + + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.pc = (unsigned long)return_address; /* dummy */ + + walk_stackframe(&frame, save_return_addr, &data); + + if (!data.level) + return data.addr; + else + return NULL; +} +EXPORT_SYMBOL_GPL(return_address);
Recordmcount utility under scripts is run, after compiling each object, to find out all the locations of calling _mcount() and put them into specific seciton named __mcount_loc. Then linker collects all such information into a table in the kernel image (between __start_mcount_loc and __stop_mcount_loc) for later use by ftrace.
This patch adds arm64 specific definitions to identify such locations. There are two types of implementation, C and Perl. On arm64, only C version is used to build the kernel now that CONFIG_HAVE_C_RECORDMCOUNT is on. But Perl version is also maintained.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 ++ scripts/recordmcount.c | 4 ++++ scripts/recordmcount.pl | 5 +++++ 3 files changed, 11 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b176bc0..6954959 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -27,6 +27,7 @@ config ARM64 select HARDIRQS_SW_RESEND select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_TRACEHOOK + select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG @@ -34,6 +35,7 @@ config ARM64 select HAVE_DMA_CONTIGUOUS select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 9c22317..b92d9f7 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -347,6 +347,10 @@ do_file(char const *const fname) case EM_ARM: reltype = R_ARM_ABS32; altmcount = "__gnu_mcount_nc"; break; + case EM_AARCH64: + reltype = R_AARCH64_ABS64; + altmcount = "_mcount"; + break; case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; case EM_METAG: reltype = R_METAG_ADDR32; altmcount = "_mcount_wrapper"; diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 91280b8..397b6b8 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$";
+} elsif ($arch eq "arm64") { + $alignment = 3; + $section_type = '%progbits'; + $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$"; + $type = ".quad"; } elsif ($arch eq "ia64") { $mcount_regex = "^\s*([0-9a-fA-F]+):.*\s_mcount$"; $type = "data8";
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 52 ++++++++++++++++++++++---------------- 4 files changed, 34 insertions(+), 22 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6954959..b1dcdb4 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -43,6 +43,7 @@ config ARM64 select HAVE_MEMBLOCK select HAVE_PATA_PLATFORM select HAVE_PERF_EVENTS + select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@
#include <linux/err.h>
+extern const void *sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h> + +#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 6a8928b..dc90f9f 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -41,6 +41,9 @@ #include <asm/traps.h> #include <asm/system_misc.h>
+#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> + /* * TODO: does not yet catch signals sent when the child dies. * in exit.c or in signal.c. @@ -1062,31 +1065,36 @@ asmlinkage int syscall_trace(int dir, struct pt_regs *regs) { unsigned long saved_reg;
- if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return regs->syscallno; + if (dir && unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) + trace_sys_exit(regs, regs_return_value(regs)); + + if (test_thread_flag(TIF_SYSCALL_TRACE)) { + if (is_compat_task()) { + /* AArch32 uses ip (r12) for scratch */ + saved_reg = regs->regs[12]; + regs->regs[12] = dir; + } else { + /* + * Save X7. X7 is used to denote syscall entry/exit: + * X7 = 0 -> entry, = 1 -> exit + */ + saved_reg = regs->regs[7]; + regs->regs[7] = dir; + }
- if (is_compat_task()) { - /* AArch32 uses ip (r12) for scratch */ - saved_reg = regs->regs[12]; - regs->regs[12] = dir; - } else { - /* - * Save X7. X7 is used to denote syscall entry/exit: - * X7 = 0 -> entry, = 1 -> exit - */ - saved_reg = regs->regs[7]; - regs->regs[7] = dir; - } + if (dir) + tracehook_report_syscall_exit(regs, 0); + else if (tracehook_report_syscall_entry(regs)) + regs->syscallno = ~0UL;
- if (dir) - tracehook_report_syscall_exit(regs, 0); - else if (tracehook_report_syscall_entry(regs)) - regs->syscallno = ~0UL; + if (is_compat_task()) + regs->regs[12] = saved_reg; + else + regs->regs[7] = saved_reg; + }
- if (is_compat_task()) - regs->regs[12] = saved_reg; - else - regs->regs[7] = saved_reg; + if (!dir && unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) + trace_sys_enter(regs, regs->syscallno);
return regs->syscallno; }
walk_stackframe() calls unwind_frame(), and if walk_stackframe() is "notrace", unwind_frame() should be also "notrace".
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index c3b6c63..54122c4 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -35,7 +35,7 @@ * ldp x29, x30, [sp] * add sp, sp, #0x10 */ -int unwind_frame(struct stackframe *frame) +int notrace unwind_frame(struct stackframe *frame) { unsigned long high, low; unsigned long fp = frame->fp;
Since insn.h is indirectly included in asm/entry-ftrace.S, we need to exclude some declarations by __ASSEMBLY__.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/insn.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index c44ad39..dc1f73b 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -21,6 +21,7 @@ /* A64 instructions are always 32 bits. */ #define AARCH64_INSN_SIZE 4
+#ifndef __ASSEMBLY__ /* * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a * Section C3.1 "A64 instruction index by encoding": @@ -104,5 +105,6 @@ bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); int aarch64_insn_patch_text_nosync(void *addr, u32 insn); int aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt); int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt); +#endif /* __ASSEMBLY__ */
#endif /* __ASM_INSN_H */
Hi Akashi,
On Fri, Feb 28, 2014 at 05:18:37AM +0000, AKASHI Takahiro wrote:
This patchset implements a function tracer on arm64. There was another implementation from Cavium network, but both of us agreed to use my patchset as future base. He is supposed to review this code, too.
The only issue that I had some concern on was "fault protection" code in prepare_ftrace_return(). With discussions with Steven and Tim (as author of arm ftrace), I removed that code since I'm not quite sure about possibility of "fault" occurrences in this function.
The code is tested on ARMv8 Fast Model with the following tracers & events: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint irqsoff & preemptirqsoff (which use CALLER_ADDRx) and also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Prerequisites are:
- "arm64: Add regs_return_value() in syscall.h"
- "arm64: make a single hook to syscall_trace() for all syscall features" patch
It looks like there might be some more dependencies that that. Do you have a branch anywhere containing this series, along with all the dependencies so I can have a play?
Cheers,
Will
Hi,
On 03/11/2014 11:35 PM, Will Deacon wrote:
Hi Akashi,
On Fri, Feb 28, 2014 at 05:18:37AM +0000, AKASHI Takahiro wrote:
This patchset implements a function tracer on arm64. There was another implementation from Cavium network, but both of us agreed to use my patchset as future base. He is supposed to review this code, too.
The only issue that I had some concern on was "fault protection" code in prepare_ftrace_return(). With discussions with Steven and Tim (as author of arm ftrace), I removed that code since I'm not quite sure about possibility of "fault" occurrences in this function.
The code is tested on ARMv8 Fast Model with the following tracers & events: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint irqsoff & preemptirqsoff (which use CALLER_ADDRx) and also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Prerequisites are:
- "arm64: Add regs_return_value() in syscall.h"
- "arm64: make a single hook to syscall_trace() for all syscall features" patch
It looks like there might be some more dependencies that that. Do you have a branch anywhere containing this series, along with all the dependencies so I can have a play?
I think you saw the following messages:
HOSTCC scripts/recordmcount /.../linux-aarch64/scripts/recordmcount.c: In function 'do_file': /.../linux-aarch64/scripts/recordmcount.c:350:7: error: 'EM_AARCH64' undeclared (first use in this function) /.../linux-aarch64/scripts/recordmcount.c:350:7: note: each undeclared identifier is reported only once for each function it appears in /.../linux-aarch64/scripts/recordmcount.c:351:15: error: 'R_AARCH64_ABS64' undeclared (first > use in this function) make[2]: *** [scripts/recordmcount] Error 1 make[1]: *** [scripts] Error 2 make: *** [sub-make] Error 2
This happens when a header file, elf.h, on "your host machine" does not have definitions of EM_AARCH64 nor R_AARCH64_ABS64 because "recordmcount" is a binary utility on host(x86), not target. It is very likely for most distros. (I mentioned this in the cover letter, but the description might not be clear.)
Possible solutions are: 1) Define both macros directly in scripts/recordmcount.c 2) Use perl version of recordmcount
Currently I take 1), but it is a workaround. 2) should work with my current patch, too. But you need to remove HAVE_C_RECORDCOUNT from arm64/Kconfig.
Which one do you prefer? I will include a fix for 1) in the next revision, anyway.
Thanks, -Takahiro AKASHI
Cheers,
Will
On Wed, 12 Mar 2014 10:47:20 +0900 AKASHI Takahiro takahiro.akashi@linaro.org wrote:
This happens when a header file, elf.h, on "your host machine" does not have definitions of EM_AARCH64 nor R_AARCH64_ABS64 because "recordmcount" is a binary utility on host(x86), not target. It is very likely for most distros. (I mentioned this in the cover letter, but the description might not be clear.)
Possible solutions are:
- Define both macros directly in scripts/recordmcount.c
- Use perl version of recordmcount
Currently I take 1), but it is a workaround. 2) should work with my current patch, too. But you need to remove HAVE_C_RECORDCOUNT from arm64/Kconfig.
Which one do you prefer? I will include a fix for 1) in the next revision, anyway.
I'd recommend #1, where you add this:
/* AARCH64 is new, and most headers don't define it. */ #ifndef EM_AARCH64 #define EM_AARCH64 ## #define R_AARCH64_ABS64 ## #endif
Obviously fill in the blank for ##
-- Steve
On Wed, 12 Mar 2014 10:47:20 +0900 AKASHI Takahiro takahiro.akashi@linaro.org wrote:
This happens when a header file, elf.h, on "your host machine" does not have definitions of EM_AARCH64 nor R_AARCH64_ABS64 because "recordmcount" is a binary utility on host(x86), not target. It is very likely for most distros. (I mentioned this in the cover letter, but the description might not be clear.)
Possible solutions are:
- Define both macros directly in scripts/recordmcount.c
- Use perl version of recordmcount
Currently I take 1), but it is a workaround. 2) should work with my current patch, too. But you need to remove HAVE_C_RECORDCOUNT from arm64/Kconfig.
Which one do you prefer? I will include a fix for 1) in the next revision, anyway.
Or even option 3)
3) I can update the main Makefile to check to see if EM_<ARCH> exists in the elf.h header, and if not, we use the perl version and undef HAVE_C_RECORDMCOUNT.
-- Steve
This patchset implements a function tracer on arm64. There was another implementation from Cavium network, but both of us agreed to use my patchset as future base. He is supposed to review this code, too.
The only issue that I had some concern on was "fault protection" code in prepare_ftrace_return(). With discussions with Steven and Tim (as author of arm ftrace), I removed that code since I'm not quite sure about possibility of "fault" occurrences in this function.
The code is tested on ARMv8 Fast Model with the following tracers & events: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint (but only for AArch64 tasks) irqsoff & preemptirqsoff (which use CALLER_ADDRx) and also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Prerequisites are: * "arm64: make a single hook to syscall_trace() for all syscall features" patch * "arm64: split syscall_trace() into separate functions for enter/exit" patch * "arm64: Add regs_return_value() in syscall.h" patch
Please be careful: * Patch [3/7] gets warnings from checkpatch, but they are based on the original's coding style. * Patch [7/7] may conflict with my audit patch because both changes the same location in syscall_trace_enter/exit(). I expect the functions are called in this order: On entry, * tracehook_report_syscall(ENTER) * trace_sys_enter() * audit_syscall_entry() On exit, * audit_sysscall_exit() * trace_sys_exit() * tracehook_report_syscall(EXIT)
Changes from v5 to v6: * changed the order of patches to avoid any bisect error (I have not tried though) * added EM_AARCH64 and R_AARCH64_ABS64 definitions in scripts/recordmcount.c just in case elf.h on host machine doesn't have them. [3/7] * updated a frame pointer (x29) in _mcount() to make it look like a normal function [4/7] * aligned with the patch, "arm64: split syscall_trace() into separate functions for enter/exit" [7/7] * defined ARCH_TRACE_IGNORE_COMPAT_SYSCALLS in order not to trace compat syscalls [7/7]
Chnages from v4 to v5: * improved the description of stack layout [1/7] * aligned with the change in "arm64: make a single hook to syscall_trace() for all syscall features" v3 [5/7]
Changes from v3 to v4: * removed unnecessary "#ifdef" [1,2/7] * changed stack depth from 48B to 16B in mcount()/ftrace_caller() (a bug) [1/7] * changed MCOUNT_INSN_SIZE to AARCH64_INSN_SIZE [1,7/7] * added a guard againt TIF_SYSCALL_TRACEPOINT [5/7] * corrected the second argument passed to trace_sys_exit() (a bug) [5/7] * aligned with the change in "arm64: make a single hook to syscall_trace() for all syscall features" v2 [5/7]
Changes from v2 to v3: * optimized register usages in asm (by not saving x0, x1, and x2) * removed "fault protection" code in prepare_ftrace_return() * rewrote ftrace_modify_code() using "hotpatch" interfaces * revised descriptions in comments
Changes from v1 to v2: * splitted one patch into some pieces for easier review (especially function tracer + dynamic ftrace + CALLER_ADDRx) * put return_address() in a separate file * renamed __mcount to _mcount (it was my mistake) * changed stackframe handling to get parent's frame pointer * removed ARCH_SUPPORTS_FTRACE_OPS * switched to "hotpatch" interfaces from Huawai * revised descriptions in comments
AKASHI Takahiro (7): arm64: add __ASSEMBLY__ in asm/insn.h arm64: Add 'notrace' attribute to unwind_frame() for ftrace ftrace: Add arm64 support to recordmcount arm64: Add ftrace support arm64: ftrace: Add dynamic ftrace support arm64: ftrace: Add CALLER_ADDRx macros arm64: ftrace: Add system call tracepoint
arch/arm64/Kconfig | 6 + arch/arm64/include/asm/ftrace.h | 69 ++++++++++++ arch/arm64/include/asm/insn.h | 2 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 + arch/arm64/kernel/Makefile | 7 +- arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 218 ++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 177 +++++++++++++++++++++++++++++ arch/arm64/kernel/ptrace.c | 48 ++++---- arch/arm64/kernel/return_address.c | 55 +++++++++ arch/arm64/kernel/stacktrace.c | 2 +- scripts/recordmcount.c | 9 ++ scripts/recordmcount.pl | 5 + 14 files changed, 583 insertions(+), 22 deletions(-) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c create mode 100644 arch/arm64/kernel/return_address.c
Since insn.h is indirectly included in asm/entry-ftrace.S, we need to exclude some declarations by __ASSEMBLY__.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/insn.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index c44ad39..dc1f73b 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -21,6 +21,7 @@ /* A64 instructions are always 32 bits. */ #define AARCH64_INSN_SIZE 4
+#ifndef __ASSEMBLY__ /* * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a * Section C3.1 "A64 instruction index by encoding": @@ -104,5 +105,6 @@ bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); int aarch64_insn_patch_text_nosync(void *addr, u32 insn); int aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt); int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt); +#endif /* __ASSEMBLY__ */
#endif /* __ASM_INSN_H */
On Thu, Mar 13, 2014 at 10:13:44AM +0000, AKASHI Takahiro wrote:
Since insn.h is indirectly included in asm/entry-ftrace.S, we need to exclude some declarations by __ASSEMBLY__.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Acked-by: Will Deacon will.deacon@arm.com
Will
walk_stackframe() calls unwind_frame(), and if walk_stackframe() is "notrace", unwind_frame() should be also "notrace".
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index c3b6c63..54122c4 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -35,7 +35,7 @@ * ldp x29, x30, [sp] * add sp, sp, #0x10 */ -int unwind_frame(struct stackframe *frame) +int notrace unwind_frame(struct stackframe *frame) { unsigned long high, low; unsigned long fp = frame->fp;
On Thu, Mar 13, 2014 at 10:13:45AM +0000, AKASHI Takahiro wrote:
walk_stackframe() calls unwind_frame(), and if walk_stackframe() is "notrace", unwind_frame() should be also "notrace".
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Acked-by: Will Deacon will.deacon@arm.com
Will
Recordmcount utility under scripts is run, after compiling each object, to find out all the locations of calling _mcount() and put them into specific seciton named __mcount_loc. Then linker collects all such information into a table in the kernel image (between __start_mcount_loc and __stop_mcount_loc) for later use by ftrace.
This patch adds arm64 specific definitions to identify such locations. There are two types of implementation, C and Perl. On arm64, only C version is used to build the kernel now that CONFIG_HAVE_C_RECORDMCOUNT is on. But Perl version is also maintained.
This patch also contains a workaround just in case where a header file, elf.h, on host machine doesn't have definitions of EM_AARCH64 nor R_AARCH64_ABS64. Without them, compiling C version of recordmcount will fail.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 ++ scripts/recordmcount.c | 9 +++++++++ scripts/recordmcount.pl | 5 +++++ 3 files changed, 16 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 27bbcfc..340e344 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -27,12 +27,14 @@ config ARM64 select HARDIRQS_SW_RESEND select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_TRACEHOOK + select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FTRACE_MCOUNT_RECORD select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 9c22317..047c8cd 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -40,6 +40,11 @@ #define R_METAG_NONE 3 #endif
+#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#define R_AARCH64_ABS64 257 +#endif + static int fd_map; /* File descriptor for file being modified. */ static int mmap_failed; /* Boolean flag. */ static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */ @@ -347,6 +352,10 @@ do_file(char const *const fname) case EM_ARM: reltype = R_ARM_ABS32; altmcount = "__gnu_mcount_nc"; break; + case EM_AARCH64: + reltype = R_AARCH64_ABS64; + altmcount = "_mcount"; + break; case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; case EM_METAG: reltype = R_METAG_ADDR32; altmcount = "_mcount_wrapper"; diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 91280b8..397b6b8 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$";
+} elsif ($arch eq "arm64") { + $alignment = 3; + $section_type = '%progbits'; + $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$"; + $type = ".quad"; } elsif ($arch eq "ia64") { $mcount_regex = "^\s*([0-9a-fA-F]+):.*\s_mcount$"; $type = "data8";
On Thu, Mar 13, 2014 at 10:13:46AM +0000, AKASHI Takahiro wrote:
Recordmcount utility under scripts is run, after compiling each object, to find out all the locations of calling _mcount() and put them into specific seciton named __mcount_loc. Then linker collects all such information into a table in the kernel image (between __start_mcount_loc and __stop_mcount_loc) for later use by ftrace.
This patch adds arm64 specific definitions to identify such locations. There are two types of implementation, C and Perl. On arm64, only C version is used to build the kernel now that CONFIG_HAVE_C_RECORDMCOUNT is on. But Perl version is also maintained.
This patch also contains a workaround just in case where a header file, elf.h, on host machine doesn't have definitions of EM_AARCH64 nor R_AARCH64_ABS64. Without them, compiling C version of recordmcount will fail.
[...]
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 9c22317..047c8cd 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -40,6 +40,11 @@ #define R_METAG_NONE 3 #endif +#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#define R_AARCH64_ABS64 257 +#endif
static int fd_map; /* File descriptor for file being modified. */ static int mmap_failed; /* Boolean flag. */ static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */ @@ -347,6 +352,10 @@ do_file(char const *const fname) case EM_ARM: reltype = R_ARM_ABS32; altmcount = "__gnu_mcount_nc"; break;
- case EM_AARCH64:
reltype = R_AARCH64_ABS64;
altmcount = "_mcount";
Shouldn't you be setting gpfx = '_' instead of overriding altmcount like this?
case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; case EM_METAG: reltype = R_METAG_ADDR32; altmcount = "_mcount_wrapper";break;
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 91280b8..397b6b8 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$"; +} elsif ($arch eq "arm64") {
- $alignment = 3;
- $section_type = '%progbits';
- $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$";
What about "mcount" (i.e. no underscore)?
Will
On 03/13/2014 09:38 PM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:46AM +0000, AKASHI Takahiro wrote:
Recordmcount utility under scripts is run, after compiling each object, to find out all the locations of calling _mcount() and put them into specific seciton named __mcount_loc. Then linker collects all such information into a table in the kernel image (between __start_mcount_loc and __stop_mcount_loc) for later use by ftrace.
This patch adds arm64 specific definitions to identify such locations. There are two types of implementation, C and Perl. On arm64, only C version is used to build the kernel now that CONFIG_HAVE_C_RECORDMCOUNT is on. But Perl version is also maintained.
This patch also contains a workaround just in case where a header file, elf.h, on host machine doesn't have definitions of EM_AARCH64 nor R_AARCH64_ABS64. Without them, compiling C version of recordmcount will fail.
[...]
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 9c22317..047c8cd 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -40,6 +40,11 @@ #define R_METAG_NONE 3 #endif
+#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#define R_AARCH64_ABS64 257 +#endif
- static int fd_map; /* File descriptor for file being modified. */ static int mmap_failed; /* Boolean flag. */ static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */
@@ -347,6 +352,10 @@ do_file(char const *const fname) case EM_ARM: reltype = R_ARM_ABS32; altmcount = "__gnu_mcount_nc"; break;
- case EM_AARCH64:
reltype = R_AARCH64_ABS64;
altmcount = "_mcount";
Shouldn't you be setting gpfx = '_' instead of overriding altmcount like this?
Yep. Fix it.
case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; case EM_METAG: reltype = R_METAG_ADDR32; altmcount = "_mcount_wrapper";break;
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 91280b8..397b6b8 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$";
+} elsif ($arch eq "arm64") {
- $alignment = 3;
- $section_type = '%progbits';
- $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$";
What about "mcount" (i.e. no underscore)?
I don't think it works since such regex doesn't match to "CALL26 _mcount".
Thanks, -Takahiro AKASHI
Will
On Thu, Mar 13, 2014 at 02:57:58PM +0000, AKASHI Takahiro wrote:
On 03/13/2014 09:38 PM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:46AM +0000, AKASHI Takahiro wrote:
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 91280b8..397b6b8 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$";
+} elsif ($arch eq "arm64") {
- $alignment = 3;
- $section_type = '%progbits';
- $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$";
What about "mcount" (i.e. no underscore)?
I don't think it works since such regex doesn't match to "CALL26 _mcount".
What I meant was, do you need to match "mcount" as well? However, after speaking to our GCC guys, they say they only support "_mcount" (preferred) and "__mcount" (which GCC itself doesn't emit) so you can leave this regex as-is.
Will
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Reviewed-by: Ganapatrao Kulkarni ganapatrao.kulkarni@cavium.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/ftrace.h | 23 +++++ arch/arm64/kernel/Makefile | 4 + arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 175 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 64 ++++++++++++++ 6 files changed, 272 insertions(+) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 340e344..6b3fef6 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -35,6 +35,8 @@ config ARM64 select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FTRACE_MCOUNT_RECORD + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..58ea595 --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/* + * arch/arm64/include/asm/ftrace.h + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H + +#include <asm/insn.h> + +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE + +#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ac67fd0 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,9 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg + # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ @@ -13,6 +16,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o +arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..0ac31c8 --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,175 @@ +/* + * arch/arm64/kernel/entry-ftrace.S + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +/* + * Gcc with -pg will put the following code in the beginning of each function: + * mov x0, x30 + * bl _mcount + * [function's body ...] + * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic + * ftrace is enabled. + * + * Please note that x0 as an argument will not be used here because we can + * get lr(x30) of instrumented function at any time by winding up call stack + * as long as the kernel is compiled without -fomit-frame-pointer. + * (or CONFIG_FRAME_POINTER, this is forced on arm64) + * + * stack layout after mcount_enter in _mcount(): + * + * current sp/fp => 0:+-----+ + * in _mcount() | x29 | -> instrumented function's fp + * +-----+ + * | x30 | -> _mcount()'s lr (= instrumented function's pc) + * old sp => +16:+-----+ + * when instrumented | | + * function calls | ... | + * _mcount() | | + * | | + * instrumented => +xx:+-----+ + * function's fp | x29 | -> parent's fp + * +-----+ + * | x30 | -> instrumented function's lr (= parent's pc) + * +-----+ + * | ... | + */ + + .macro mcount_enter + stp x29, x30, [sp, #-16]! + mov x29, sp + .endm + + .macro mcount_exit + ldp x29, x30, [sp], #16 + ret + .endm + + .macro mcount_adjust_addr rd, rn + sub \rd, \rn, #AARCH64_INSN_SIZE + .endm + + /* for instrumented function's parent */ + .macro mcount_get_parent_fp reg + ldr \reg, [x29] + ldr \reg, [\reg] + .endm + + /* for instrumented function */ + .macro mcount_get_pc0 reg + mcount_adjust_addr \reg, x30 + .endm + + .macro mcount_get_pc reg + ldr \reg, [x29, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr reg + ldr \reg, [x29] + ldr \reg, [\reg, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr_addr reg + ldr \reg, [x29] + add \reg, \reg, #8 + .endm + +/* + * void _mcount(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function makes calls, if enabled, to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST + ldr x0, =ftrace_trace_stop + ldr x0, [x0] // if ftrace_trace_stop + ret // return; +#endif + mcount_enter + + ldr x0, =ftrace_trace_function + ldr x2, [x0] + adr x0, ftrace_stub + cmp x0, x2 // if (ftrace_trace_function + b.eq skip_ftrace_call // != ftrace_stub) { + + mcount_get_pc x0 // function's pc + mcount_get_lr x1 // function's lr (= parent's pc) + blr x2 // (*ftrace_trace_function)(pc, lr); + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call: // return; + mcount_exit // } +#else + mcount_exit // return; + // } +skip_ftrace_call: + ldr x1, =ftrace_graph_return + ldr x2, [x1] // if ((ftrace_graph_return + cmp x0, x2 // != ftrace_stub) + b.ne ftrace_graph_caller + + ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry + ldr x2, [x1] // != ftrace_graph_entry_stub)) + ldr x0, =ftrace_graph_entry_stub + cmp x0, x2 + b.ne ftrace_graph_caller // ftrace_graph_caller(); + + mcount_exit +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount) + +ENTRY(ftrace_stub) + ret +ENDPROC(ftrace_stub) + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * void ftrace_graph_caller(void) + * + * Called from _mcount() or ftrace_caller() when function_graph tracer is + * selected. + * This function w/ prepare_ftrace_return() fakes link register's value on + * the call stack in order to intercept instrumented function's return path + * and run return_to_handler() later on its exit. + */ +ENTRY(ftrace_graph_caller) + mcount_get_lr_addr x0 // pointer to function's saved lr + mcount_get_pc x1 // function's pc + mcount_get_parent_fp x2 // parent's fp + bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp) + + mcount_exit +ENDPROC(ftrace_graph_caller) + +/* + * void return_to_handler(void) + * + * Run ftrace_return_to_handler() before going back to parent. + * @fp is checked against the value passed by ftrace_graph_caller() + * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled. + */ + .global return_to_handler +return_to_handler: + str x0, [sp, #-16]! + mov x0, x29 // parent's fp + bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); + mov x30, x0 // restore the original return address + ldr x0, [sp], #16 + ret +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/* + * arch/arm64/kernel/ftrace.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * function_graph tracer expects ftrace_return_to_handler() to be called + * on the way back to parent. For this purpose, this function is called + * in _mcount() or ftrace_caller() to replace return address (*parent) on + * the call stack to return_to_handler. + * + * Note that @frame_pointer is used only for sanity check later. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + unsigned long frame_pointer) +{ + unsigned long return_hooker = (unsigned long)&return_to_handler; + unsigned long old; + struct ftrace_graph_ent trace; + int err; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + + /* + * Note: + * No protection against faulting at *parent, which may be seen + * on other archs. It's unlikely on AArch64. + */ + old = *parent; + *parent = return_hooker; + + trace.func = self_addr; + trace.depth = current->curr_ret_stack + 1; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + *parent = old; + return; + } + + err = ftrace_push_return_trace(old, self_addr, &trace.depth, + frame_pointer); + if (err == -EBUSY) { + *parent = old; + return; + } +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
On Thu, Mar 13, 2014 at 10:13:47AM +0000, AKASHI Takahiro wrote:
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
[...]
diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..0ac31c8 --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,175 @@ +/*
- arch/arm64/kernel/entry-ftrace.S
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h>
+/*
- Gcc with -pg will put the following code in the beginning of each function:
mov x0, x30
bl _mcount
[function's body ...]
- "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic
- ftrace is enabled.
- Please note that x0 as an argument will not be used here because we can
- get lr(x30) of instrumented function at any time by winding up call stack
- as long as the kernel is compiled without -fomit-frame-pointer.
- (or CONFIG_FRAME_POINTER, this is forced on arm64)
- stack layout after mcount_enter in _mcount():
- current sp/fp => 0:+-----+
- in _mcount() | x29 | -> instrumented function's fp
+-----+
| x30 | -> _mcount()'s lr (= instrumented function's pc)
- old sp => +16:+-----+
- when instrumented | |
- function calls | ... |
- _mcount() | |
| |
- instrumented => +xx:+-----+
- function's fp | x29 | -> parent's fp
+-----+
| x30 | -> instrumented function's lr (= parent's pc)
+-----+
| ... |
I guess it's just the diff that's misaligning your ASCII art here?
+/*
- void return_to_handler(void)
- Run ftrace_return_to_handler() before going back to parent.
- @fp is checked against the value passed by ftrace_graph_caller()
- only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled.
- */
.global return_to_handler
+return_to_handler:
ENTRY(return_to_handler)
str x0, [sp, #-16]!
mov x0, x29 // parent's fp
bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp);
mov x30, x0 // restore the original return address
ldr x0, [sp], #16
ret
and an ENDPROC here.
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/*
- arch/arm64/kernel/ftrace.c
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h>
+#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h>
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/*
- function_graph tracer expects ftrace_return_to_handler() to be called
- on the way back to parent. For this purpose, this function is called
- in _mcount() or ftrace_caller() to replace return address (*parent) on
- the call stack to return_to_handler.
- Note that @frame_pointer is used only for sanity check later.
- */
+void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
unsigned long frame_pointer)
+{
unsigned long return_hooker = (unsigned long)&return_to_handler;
unsigned long old;
struct ftrace_graph_ent trace;
int err;
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
return;
/*
* Note:
* No protection against faulting at *parent, which may be seen
* on other archs. It's unlikely on AArch64.
*/
old = *parent;
*parent = return_hooker;
return_hook? People might take it personally otherwise ;)
trace.func = self_addr;
in kernel/ftrace/ftrace.c there's an smb_wmb() between setting up tracing_graph_pause and setting the ret_stack with a comment saying:
/* Make sure the tasks see the -1 first: */
Why don't we have a corresponding read-barrier here?
Will
On Thu, 2014-03-13 at 17:08 +0000, Will Deacon wrote:
/*
* Note:
* No protection against faulting at *parent, which may be seen
* on other archs. It's unlikely on AArch64.
*/
old = *parent;
*parent = return_hooker;
return_hook? People might take it personally otherwise ;)
No, return_hooker is consistent with all the other archs. Hey, it's a rugby position! Note, which I was when I played. ;-)
trace.func = self_addr;
in kernel/ftrace/ftrace.c there's an smb_wmb() between setting up tracing_graph_pause and setting the ret_stack with a comment saying:
/* Make sure the tasks see the -1 first: */
Why don't we have a corresponding read-barrier here?
The corresponding rmb is in kernel/trace/trace_function_graph ftrace_push_return_trace().
-- Steve
On Thu, Mar 13, 2014 at 06:27:39PM +0000, Steven Rostedt wrote:
On Thu, 2014-03-13 at 17:08 +0000, Will Deacon wrote:
/*
* Note:
* No protection against faulting at *parent, which may be seen
* on other archs. It's unlikely on AArch64.
*/
old = *parent;
*parent = return_hooker;
return_hook? People might take it personally otherwise ;)
No, return_hooker is consistent with all the other archs. Hey, it's a rugby position! Note, which I was when I played. ;-)
Hehe, in which case your children will be able to execute that line of ftrace!
trace.func = self_addr;
in kernel/ftrace/ftrace.c there's an smb_wmb() between setting up tracing_graph_pause and setting the ret_stack with a comment saying:
/* Make sure the tasks see the -1 first: */
Why don't we have a corresponding read-barrier here?
The corresponding rmb is in kernel/trace/trace_function_graph ftrace_push_return_trace().
Okey doke then, I guess we don't really care about tracing_graph_pause getting out-of-sync with curr_ret_stack.
Will
On Thu, 2014-03-13 at 18:37 +0000, Will Deacon wrote:
No, return_hooker is consistent with all the other archs. Hey, it's a rugby position! Note, which I was when I played. ;-)
Hehe, in which case your children will be able to execute that line of ftrace!
My kids already know I was :-)
trace.func = self_addr;
in kernel/ftrace/ftrace.c there's an smb_wmb() between setting up tracing_graph_pause and setting the ret_stack with a comment saying:
/* Make sure the tasks see the -1 first: */
Why don't we have a corresponding read-barrier here?
The corresponding rmb is in kernel/trace/trace_function_graph ftrace_push_return_trace().
Okey doke then, I guess we don't really care about tracing_graph_pause getting out-of-sync with curr_ret_stack.
Yep, the tracing_graph_pause is more to prevent crazy stuff happening when an interrupt comes in. So it is just local cpu context, and doesn't have issues with other CPUS. If it takes a bit for the task to see it go to zero, the worse thing is that you might miss a few tracing functions until that gets synced. Which will probably happen by the time tracing is fully activated anyway.
-- Steve
On 03/14/2014 02:08 AM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:47AM +0000, AKASHI Takahiro wrote:
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
[...]
You seem not to like this statement :-)
diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..0ac31c8 --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,175 @@ +/*
- arch/arm64/kernel/entry-ftrace.S
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h>
+/*
- Gcc with -pg will put the following code in the beginning of each function:
mov x0, x30
bl _mcount
[function's body ...]
- "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic
- ftrace is enabled.
- Please note that x0 as an argument will not be used here because we can
- get lr(x30) of instrumented function at any time by winding up call stack
- as long as the kernel is compiled without -fomit-frame-pointer.
- (or CONFIG_FRAME_POINTER, this is forced on arm64)
- stack layout after mcount_enter in _mcount():
- current sp/fp => 0:+-----+
- in _mcount() | x29 | -> instrumented function's fp
+-----+
| x30 | -> _mcount()'s lr (= instrumented function's pc)
- old sp => +16:+-----+
- when instrumented | |
- function calls | ... |
- _mcount() | |
| |
- instrumented => +xx:+--- --+
- function's fp | x29 | -> parent's fp
+-----+
| x30 | -> instrumented function's lr (= parent's pc)
+-----+
| ... |
I guess it's just the diff that's misaligning your ASCII art here?
Yes, I think so. Misaligned due to "tab"
+/*
- void return_to_handler(void)
- Run ftrace_return_to_handler() before going back to parent.
- @fp is checked against the value passed by ftrace_graph_caller()
- only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled.a
- */
.global return_to_handler
+return_to_handler:
ENTRY(return_to_handler)
Fix it.
str x0, [sp, #-16]!
mov x0, x29 // parent's fp
bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp);
mov x30, x0 // restore the original return address
ldr x0, [sp], #16
ret
and an ENDPROC here.
Fix it. But please note that this (return_to_handler) is not a real function. Declaring it as ENDPROC is not very useful.
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/*
- arch/arm64/kernel/ftrace.c
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h>
+#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h>
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/*
- function_graph tracer expects ftrace_return_to_handler() to be called
- on the way back to parent. For this purpose, this function is called
- in _mcount() or ftrace_caller() to replace return address (*parent) on
- the call stack to return_to_handler.
- Note that @frame_pointer is used only for sanity check later.
- */
+void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
unsigned long frame_pointer)
+{
unsigned long return_hooker = (unsigned long)&return_to_handler;
unsigned long old;
struct ftrace_graph_ent trace;
int err;
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
return;
/*
* Note:
* No protection against faulting at *parent, which may be seen
* on other archs. It's unlikely on AArch64.
*/
old = *parent;
*parent = return_hooker;
return_hook? People might take it personally otherwise ;)
I didn't know what "(return) hooker" means. Anyway, leave it as it is after you and Steven's discussion.
trace.func = self_addr;
in kernel/ftrace/ftrace.c there's an smb_wmb() between setting up tracing_graph_pause and setting the ret_stack with a comment saying:
/* Make sure the tasks see the -1 first: */
Why don't we have a corresponding read-barrier here?
I think Steven answered it.
-Takahiro AKASHI
Will
On Fri, Mar 14, 2014 at 04:45:13AM +0000, AKASHI Takahiro wrote:
On 03/14/2014 02:08 AM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:47AM +0000, AKASHI Takahiro wrote:
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
[...]
You seem not to like this statement :-)
Huh? I was just using '[...]' to signify that I'd removed a lot of context (since the code all looked fine there). Otherwise, review emails can get really long.
str x0, [sp, #-16]!
mov x0, x29 // parent's fp
bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp);
mov x30, x0 // restore the original return address
ldr x0, [sp], #16
ret
and an ENDPROC here.
Fix it. But please note that this (return_to_handler) is not a real function. Declaring it as ENDPROC is not very useful.
Then use END(...). The opening macro was the main point, since that avoid the need for an explicit .globl.
Will
On 03/14/2014 07:07 PM, Will Deacon wrote:
On Fri, Mar 14, 2014 at 04:45:13AM +0000, AKASHI Takahiro wrote:
On 03/14/2014 02:08 AM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:47AM +0000, AKASHI Takahiro wrote:
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
[...]
You seem not to like this statement :-)
Huh? I was just using '[...]' to signify that I'd removed a lot of context (since the code all looked fine there). Otherwise, review emails can get really long.
Please ignore my comment.
str x0, [sp, #-16]!
mov x0, x29 // parent's fp
bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp);
mov x30, x0 // restore the original return address
ldr x0, [sp], #16
ret
and an ENDPROC here.
Fix it. But please note that this (return_to_handler) is not a real function. Declaring it as ENDPROC is not very useful.
Then use END(...). The opening macro was the main point, since that avoid the need for an explicit .globl.
Fix it.
-Takahiro AKASHI
Will
This patch allows "dynamic ftrace" if CONFIG_DYNAMIC_FTRACE is enabled. Here we can turn on and off tracing dynamically per-function base.
On arm64, this is done by patching single branch instruction to _mcount() inserted by gcc -pg option. The branch is replaced to NOP initially at kernel start up, and later on, NOP to branch to ftrace_caller() when enabled or branch to NOP when disabled. Please note that ftrace_caller() is a counterpart of _mcount() in case of 'static' ftrace.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 15 +++++ arch/arm64/kernel/entry-ftrace.S | 43 +++++++++++++++ arch/arm64/kernel/ftrace.c | 113 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6b3fef6..6954959 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -33,6 +33,7 @@ config ARM64 select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS + select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 58ea595..ed5c448 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,21 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); + +struct dyn_arch_ftrace { + /* No extra data needed for arm64 */ +}; + +extern unsigned long ftrace_graph_call; + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ + /* + * addr is the address of the mcount call instruction. + * recordmcount does the necessary offset calculation. + */ + return addr; +} #endif /* __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S index 0ac31c8..c0fbe10 100644 --- a/arch/arm64/kernel/entry-ftrace.S +++ b/arch/arm64/kernel/entry-ftrace.S @@ -86,6 +86,7 @@ add \reg, \reg, #8 .endm
+#ifndef CONFIG_DYNAMIC_FTRACE /* * void _mcount(unsigned long return_address) * @return_address: return address to instrumented function @@ -134,6 +135,48 @@ skip_ftrace_call: #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ENDPROC(_mcount)
+#else /* CONFIG_DYNAMIC_FTRACE */ +/* + * _mcount() is used to build the kernel with -pg option, but all the branch + * instructions to _mcount() are replaced to NOP initially at kernel start up, + * and later on, NOP to branch to ftrace_caller() when enabled or branch to + * NOP when disabled per-function base. + */ +ENTRY(_mcount) + ret +ENDPROC(_mcount) + +/* + * void ftrace_caller(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function is a counterpart of _mcount() in 'static' ftrace, and + * makes calls to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(ftrace_caller) + mcount_enter + + mcount_get_pc0 x0 // function's pc + mcount_get_lr x1 // function's lr + + .global ftrace_call +ftrace_call: // tracer(pc, lr); + nop // This will be replaced with "bl xxx" + // where xxx can be any kind of tracer. + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + .global ftrace_graph_call +ftrace_graph_call: // ftrace_graph_caller(); + nop // If enabled, this will be replaced + // "b ftrace_graph_caller" +#endif + + mcount_exit +ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */ + ENTRY(ftrace_stub) ret ENDPROC(ftrace_stub) diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index a559ab8..8c26476 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -17,6 +17,89 @@ #include <asm/ftrace.h> #include <asm/insn.h>
+#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Replace a single instruction, which may be a branch or NOP. + * If @validate == true, a replaced instruction is checked against 'old'. + */ +static int ftrace_modify_code(unsigned long pc, unsigned int old, + unsigned int new, bool validate) +{ + unsigned int replaced; + + /* + * Note: + * Due to modules and __init, code can disappear and change, + * we need to protect against faulting as well as code changing. + * We do this by aarch64_insn_*() which use the probe_kernel_*(). + * + * No lock is held here because all the modifications are run + * through stop_machine(). + */ + if (validate) { + if (aarch64_insn_read((void *)pc, &replaced)) + return -EFAULT; + + if (replaced != old) + return -EINVAL; + } + if (aarch64_insn_patch_text_nosync((void *)pc, new)) + return -EPERM; + + return 0; +} + +/* + * Replace tracer function in ftrace_caller() + */ +int ftrace_update_ftrace_func(ftrace_func_t func) +{ + unsigned long pc; + unsigned int new; + + pc = (unsigned long)&ftrace_call; + new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); + + return ftrace_modify_code(pc, 0, new, false); +} + +/* + * Turn on the call to ftrace_caller() in instrumented function + */ +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_nop(); + new = aarch64_insn_gen_branch_imm(pc, addr, true); + + return ftrace_modify_code(pc, old, new, true); +} + +/* + * Turn off the call to ftrace_caller() in instrumented function + */ +int ftrace_make_nop(struct module *mod, + struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + unsigned int old, new; + + old = aarch64_insn_gen_branch_imm(pc, addr, true); + new = aarch64_insn_gen_nop(); + + return ftrace_modify_code(pc, old, new, true); +} + +int __init ftrace_dyn_arch_init(void *data) +{ + *(unsigned long *)data = 0; + + return 0; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* * function_graph tracer expects ftrace_return_to_handler() to be called @@ -61,4 +144,34 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } } + +#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Turn on/off the call to ftrace_graph_caller() in ftrace_caller() + * depending on @enable. + */ +static int ftrace_modify_graph_caller(bool enable) +{ + unsigned long pc = (unsigned long)&ftrace_graph_call; + unsigned int branch, nop, old, new; + + branch = aarch64_insn_gen_branch_imm(pc, + (unsigned long)ftrace_graph_caller, false); + nop = aarch64_insn_gen_nop(); + old = enable ? nop : branch; + new = enable ? branch : nop; + + return ftrace_modify_code(pc, old, new, true); +} + +int ftrace_enable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(true); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(false); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
On Thu, Mar 13, 2014 at 10:13:48AM +0000, AKASHI Takahiro wrote:
This patch allows "dynamic ftrace" if CONFIG_DYNAMIC_FTRACE is enabled. Here we can turn on and off tracing dynamically per-function base.
On arm64, this is done by patching single branch instruction to _mcount() inserted by gcc -pg option. The branch is replaced to NOP initially at kernel start up, and later on, NOP to branch to ftrace_caller() when enabled or branch to NOP when disabled. Please note that ftrace_caller() is a counterpart of _mcount() in case of 'static' ftrace.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 15 +++++ arch/arm64/kernel/entry-ftrace.S | 43 +++++++++++++++ arch/arm64/kernel/ftrace.c | 113 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6b3fef6..6954959 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -33,6 +33,7 @@ config ARM64 select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS
- select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 58ea595..ed5c448 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,21 @@ #ifndef __ASSEMBLY__ extern void _mcount(unsigned long);
+struct dyn_arch_ftrace {
- /* No extra data needed for arm64 */
+};
+extern unsigned long ftrace_graph_call;
+static inline unsigned long ftrace_call_adjust(unsigned long addr) +{
- /*
* addr is the address of the mcount call instruction.
* recordmcount does the necessary offset calculation.
*/
- return addr;
+}
You could just as easily implement this as a dummy macro, but I guess it doesn't matter either way.
#endif /* __ASSEMBLY__ */ #endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S index 0ac31c8..c0fbe10 100644 --- a/arch/arm64/kernel/entry-ftrace.S +++ b/arch/arm64/kernel/entry-ftrace.S @@ -86,6 +86,7 @@ add \reg, \reg, #8 .endm +#ifndef CONFIG_DYNAMIC_FTRACE /*
- void _mcount(unsigned long return_address)
- @return_address: return address to instrumented function
@@ -134,6 +135,48 @@ skip_ftrace_call: #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ENDPROC(_mcount) +#else /* CONFIG_DYNAMIC_FTRACE */ +/*
- _mcount() is used to build the kernel with -pg option, but all the branch
- instructions to _mcount() are replaced to NOP initially at kernel start up,
- and later on, NOP to branch to ftrace_caller() when enabled or branch to
- NOP when disabled per-function base.
- */
+ENTRY(_mcount)
- ret
+ENDPROC(_mcount)
Judging by your comment then, this should never be called. Is that right? If so, we could add a BUG-equivalent so we know if we missed an mcount during patching.
+/*
- void ftrace_caller(unsigned long return_address)
- @return_address: return address to instrumented function
- This function is a counterpart of _mcount() in 'static' ftrace, and
- makes calls to:
- tracer function to probe instrumented function's entry,
- ftrace_graph_caller to set up an exit hook
- */
+ENTRY(ftrace_caller)
- mcount_enter
- mcount_get_pc0 x0 // function's pc
- mcount_get_lr x1 // function's lr
- .global ftrace_call
+ftrace_call: // tracer(pc, lr);
- nop // This will be replaced with "bl xxx"
// where xxx can be any kind of tracer.
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- .global ftrace_graph_call
+ftrace_graph_call: // ftrace_graph_caller();
- nop // If enabled, this will be replaced
// "b ftrace_graph_caller"
+#endif
- mcount_exit
+ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */
ENTRY(ftrace_stub) ret ENDPROC(ftrace_stub) diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index a559ab8..8c26476 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -17,6 +17,89 @@ #include <asm/ftrace.h> #include <asm/insn.h> +#ifdef CONFIG_DYNAMIC_FTRACE +/*
- Replace a single instruction, which may be a branch or NOP.
- If @validate == true, a replaced instruction is checked against 'old'.
- */
+static int ftrace_modify_code(unsigned long pc, unsigned int old,
unsigned int new, bool validate)
+{
- unsigned int replaced;
u32 is a bit clearer for instructions.
- /*
* Note:
* Due to modules and __init, code can disappear and change,
* we need to protect against faulting as well as code changing.
* We do this by aarch64_insn_*() which use the probe_kernel_*().
*
* No lock is held here because all the modifications are run
* through stop_machine().
*/
- if (validate) {
if (aarch64_insn_read((void *)pc, &replaced))
return -EFAULT;
if (replaced != old)
return -EINVAL;
- }
- if (aarch64_insn_patch_text_nosync((void *)pc, new))
return -EPERM;
I think you're better off propagating the errors here, rather than overriding them with EFAULT/EINVAL/EPERM.
- return 0;
+}
+/*
- Replace tracer function in ftrace_caller()
- */
+int ftrace_update_ftrace_func(ftrace_func_t func) +{
- unsigned long pc;
- unsigned int new;
- pc = (unsigned long)&ftrace_call;
- new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true);
- return ftrace_modify_code(pc, 0, new, false);
+}
+/*
- Turn on the call to ftrace_caller() in instrumented function
- */
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{
- unsigned long pc = rec->ip;
- unsigned int old, new;
- old = aarch64_insn_gen_nop();
- new = aarch64_insn_gen_branch_imm(pc, addr, true);
- return ftrace_modify_code(pc, old, new, true);
+}
+/*
- Turn off the call to ftrace_caller() in instrumented function
- */
+int ftrace_make_nop(struct module *mod,
struct dyn_ftrace *rec, unsigned long addr)
+{
- unsigned long pc = rec->ip;
- unsigned int old, new;
- old = aarch64_insn_gen_branch_imm(pc, addr, true);
- new = aarch64_insn_gen_nop();
- return ftrace_modify_code(pc, old, new, true);
+}
+int __init ftrace_dyn_arch_init(void *data) +{
- *(unsigned long *)data = 0;
- return 0;
+} +#endif /* CONFIG_DYNAMIC_FTRACE */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER /*
- function_graph tracer expects ftrace_return_to_handler() to be called
@@ -61,4 +144,34 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } }
+#ifdef CONFIG_DYNAMIC_FTRACE +/*
- Turn on/off the call to ftrace_graph_caller() in ftrace_caller()
- depending on @enable.
- */
+static int ftrace_modify_graph_caller(bool enable) +{
- unsigned long pc = (unsigned long)&ftrace_graph_call;
- unsigned int branch, nop, old, new;
- branch = aarch64_insn_gen_branch_imm(pc,
(unsigned long)ftrace_graph_caller, false);
- nop = aarch64_insn_gen_nop();
- old = enable ? nop : branch;
- new = enable ? branch : nop;
- return ftrace_modify_code(pc, old, new, true);
You could rewrite this as:
if (enable) return ftrace_modify_code(pc, nop, branch, true); else return ftrace_modify_code(pc, branch, nop, true);
which I find easier to read.
Will
On Thu, 2014-03-13 at 18:10 +0000, Will Deacon wrote:
+#else /* CONFIG_DYNAMIC_FTRACE */ +/*
- _mcount() is used to build the kernel with -pg option, but all the branch
- instructions to _mcount() are replaced to NOP initially at kernel start up,
- and later on, NOP to branch to ftrace_caller() when enabled or branch to
- NOP when disabled per-function base.
- */
+ENTRY(_mcount)
- ret
+ENDPROC(_mcount)
Judging by your comment then, this should never be called. Is that right? If so, we could add a BUG-equivalent so we know if we missed an mcount during patching.
Actually, it can be called before the change to nops are done in early boot. This is done very early, but everything before ftrace_init() in init/main.c can still call _mcount.
- /*
* Note:
* Due to modules and __init, code can disappear and change,
* we need to protect against faulting as well as code changing.
* We do this by aarch64_insn_*() which use the probe_kernel_*().
*
* No lock is held here because all the modifications are run
* through stop_machine().
*/
- if (validate) {
if (aarch64_insn_read((void *)pc, &replaced))
return -EFAULT;
if (replaced != old)
return -EINVAL;
- }
- if (aarch64_insn_patch_text_nosync((void *)pc, new))
return -EPERM;
I think you're better off propagating the errors here, rather than overriding them with EFAULT/EINVAL/EPERM.
The ftrace generic code expects to see these specific errors. Look at ftrace_bug() in kernel/trace/ftrace.c.
- return 0;
+}
+/*
- Replace tracer function in ftrace_caller()
- */
+int ftrace_update_ftrace_func(ftrace_func_t func) +{
- unsigned long pc;
- unsigned int new;
- pc = (unsigned long)&ftrace_call;
- new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true);
- return ftrace_modify_code(pc, 0, new, false);
+}
+/*
- Turn on the call to ftrace_caller() in instrumented function
- */
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{
- unsigned long pc = rec->ip;
- unsigned int old, new;
- old = aarch64_insn_gen_nop();
- new = aarch64_insn_gen_branch_imm(pc, addr, true);
- return ftrace_modify_code(pc, old, new, true);
+}
+/*
- Turn off the call to ftrace_caller() in instrumented function
- */
+int ftrace_make_nop(struct module *mod,
struct dyn_ftrace *rec, unsigned long addr)
+{
- unsigned long pc = rec->ip;
- unsigned int old, new;
- old = aarch64_insn_gen_branch_imm(pc, addr, true);
- new = aarch64_insn_gen_nop();
- return ftrace_modify_code(pc, old, new, true);
+}
+int __init ftrace_dyn_arch_init(void *data) +{
- *(unsigned long *)data = 0;
- return 0;
+} +#endif /* CONFIG_DYNAMIC_FTRACE */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER /*
- function_graph tracer expects ftrace_return_to_handler() to be called
@@ -61,4 +144,34 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } }
+#ifdef CONFIG_DYNAMIC_FTRACE +/*
- Turn on/off the call to ftrace_graph_caller() in ftrace_caller()
- depending on @enable.
- */
+static int ftrace_modify_graph_caller(bool enable) +{
- unsigned long pc = (unsigned long)&ftrace_graph_call;
- unsigned int branch, nop, old, new;
- branch = aarch64_insn_gen_branch_imm(pc,
(unsigned long)ftrace_graph_caller, false);
- nop = aarch64_insn_gen_nop();
- old = enable ? nop : branch;
- new = enable ? branch : nop;
- return ftrace_modify_code(pc, old, new, true);
You could rewrite this as:
if (enable) return ftrace_modify_code(pc, nop, branch, true); else return ftrace_modify_code(pc, branch, nop, true);
which I find easier to read.
Heh, maybe that could be updated in other archs too. I'll have to think about that one.
-- Steve
Thank you for you clarification, Steven.
-Takahiro AKASHI
On 03/14/2014 03:33 AM, Steven Rostedt wrote:
On Thu, 2014-03-13 at 18:10 +0000, Will Deacon wrote:
+#else /* CONFIG_DYNAMIC_FTRACE */ +/*
- _mcount() is used to build the kernel with -pg option, but all the branch
- instructions to _mcount() are replaced to NOP initially at kernel start up,
- and later on, NOP to branch to ftrace_caller() when enabled or branch to
- NOP when disabled per-function base.
- */
+ENTRY(_mcount)
- ret
+ENDPROC(_mcount)
Judging by your comment then, this should never be called. Is that right? If so, we could add a BUG-equivalent so we know if we missed an mcount during patching.
Actually, it can be called before the change to nops are done in early boot. This is done very early, but everything before ftrace_init() in init/main.c can still call _mcount.
- /*
* Note:
* Due to modules and __init, code can disappear and change,
* we need to protect against faulting as well as code changing.
* We do this by aarch64_insn_*() which use the probe_kernel_*().
*
* No lock is held here because all the modifications are run
* through stop_machine().
*/
- if (validate) {
if (aarch64_insn_read((void *)pc, &replaced))
return -EFAULT;
if (replaced != old)
return -EINVAL;
- }
- if (aarch64_insn_patch_text_nosync((void *)pc, new))
return -EPERM;
I think you're better off propagating the errors here, rather than overriding them with EFAULT/EINVAL/EPERM.
The ftrace generic code expects to see these specific errors. Look at ftrace_bug() in kernel/trace/ftrace.c.
- return 0;
+}
+/*
- Replace tracer function in ftrace_caller()
- */
+int ftrace_update_ftrace_func(ftrace_func_t func) +{
- unsigned long pc;
- unsigned int new;
- pc = (unsigned long)&ftrace_call;
- new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true);
- return ftrace_modify_code(pc, 0, new, false);
+}
+/*
- Turn on the call to ftrace_caller() in instrumented function
- */
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{
- unsigned long pc = rec->ip;
- unsigned int old, new;
- old = aarch64_insn_gen_nop();
- new = aarch64_insn_gen_branch_imm(pc, addr, true);
- return ftrace_modify_code(pc, old, new, true);
+}
+/*
- Turn off the call to ftrace_caller() in instrumented function
- */
+int ftrace_make_nop(struct module *mod,
struct dyn_ftrace *rec, unsigned long addr)
+{
- unsigned long pc = rec->ip;
- unsigned int old, new;
- old = aarch64_insn_gen_branch_imm(pc, addr, true);
- new = aarch64_insn_gen_nop();
- return ftrace_modify_code(pc, old, new, true);
+}
+int __init ftrace_dyn_arch_init(void *data) +{
- *(unsigned long *)data = 0;
- return 0;
+} +#endif /* CONFIG_DYNAMIC_FTRACE */
- #ifdef CONFIG_FUNCTION_GRAPH_TRACER /*
- function_graph tracer expects ftrace_return_to_handler() to be called
@@ -61,4 +144,34 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } }
+#ifdef CONFIG_DYNAMIC_FTRACE +/*
- Turn on/off the call to ftrace_graph_caller() in ftrace_caller()
- depending on @enable.
- */
+static int ftrace_modify_graph_caller(bool enable) +{
- unsigned long pc = (unsigned long)&ftrace_graph_call;
- unsigned int branch, nop, old, new;
- branch = aarch64_insn_gen_branch_imm(pc,
(unsigned long)ftrace_graph_caller, false);
- nop = aarch64_insn_gen_nop();
- old = enable ? nop : branch;
- new = enable ? branch : nop;
- return ftrace_modify_code(pc, old, new, true);
You could rewrite this as:
if (enable) return ftrace_modify_code(pc, nop, branch, true); else return ftrace_modify_code(pc, branch, nop, true);
which I find easier to read.
Heh, maybe that could be updated in other archs too. I'll have to think about that one.
-- Steve
On 03/14/2014 03:10 AM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:48AM +0000, AKASHI Takahiro wrote:
This patch allows "dynamic ftrace" if CONFIG_DYNAMIC_FTRACE is enabled. Here we can turn on and off tracing dynamically per-function base.
On arm64, this is done by patching single branch instruction to _mcount() inserted by gcc -pg option. The branch is replaced to NOP initially at kernel start up, and later on, NOP to branch to ftrace_caller() when enabled or branch to NOP when disabled. Please note that ftrace_caller() is a counterpart of _mcount() in case of 'static' ftrace.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 15 +++++ arch/arm64/kernel/entry-ftrace.S | 43 +++++++++++++++ arch/arm64/kernel/ftrace.c | 113 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6b3fef6..6954959 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -33,6 +33,7 @@ config ARM64 select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS
- select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 58ea595..ed5c448 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,21 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long);
+struct dyn_arch_ftrace {
- /* No extra data needed for arm64 */
+};
+extern unsigned long ftrace_graph_call;
+static inline unsigned long ftrace_call_adjust(unsigned long addr) +{
- /*
* addr is the address of the mcount call instruction.
* recordmcount does the necessary offset calculation.
*/
- return addr;
+}
You could just as easily implement this as a dummy macro, but I guess it doesn't matter either way.
FYI, all archs define this as an inline function. Leave it as it is.
#endif /* __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S index 0ac31c8..c0fbe10 100644 --- a/arch/arm64/kernel/entry-ftrace.S +++ b/arch/arm64/kernel/entry-ftrace.S @@ -86,6 +86,7 @@ add \reg, \reg, #8 .endm
+#ifndef CONFIG_DYNAMIC_FTRACE /*
- void _mcount(unsigned long return_address)
- @return_address: return address to instrumented function
@@ -134,6 +135,48 @@ skip_ftrace_call: #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ENDPROC(_mcount)
+#else /* CONFIG_DYNAMIC_FTRACE */ +/*
- _mcount() is used to build the kernel with -pg option, but all the branch
- instructions to _mcount() are replaced to NOP initially at kernel start up,
- and later on, NOP to branch to ftrace_caller() when enabled or branch to
- NOP when disabled per-function base.
- */
+ENTRY(_mcount)
- ret
+ENDPROC(_mcount)
Judging by your comment then, this should never be called. Is that right? If so, we could add a BUG-equivalent so we know if we missed an mcount during patching.
Steven explained this.
+/*
- void ftrace_caller(unsigned long return_address)
- @return_address: return address to instrumented function
- This function is a counterpart of _mcount() in 'static' ftrace, and
- makes calls to:
- tracer function to probe instrumented function's entry,
- ftrace_graph_caller to set up an exit hook
- */
+ENTRY(ftrace_caller)
- mcount_enter
- mcount_get_pc0 x0 // function's pc
- mcount_get_lr x1 // function's lr
- .global ftrace_call
+ftrace_call: // tracer(pc, lr);
- nop // This will be replaced with "bl xxx"
// where xxx can be any kind of tracer.
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- .global ftrace_graph_call
+ftrace_graph_call: // ftrace_graph_caller();
- nop // If enabled, this will be replaced
// "b ftrace_graph_caller"
+#endif
- mcount_exit
+ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */
- ENTRY(ftrace_stub) ret ENDPROC(ftrace_stub)
diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index a559ab8..8c26476 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -17,6 +17,89 @@ #include <asm/ftrace.h> #include <asm/insn.h>
+#ifdef CONFIG_DYNAMIC_FTRACE +/*
- Replace a single instruction, which may be a branch or NOP.
- If @validate == true, a replaced instruction is checked against 'old'.
- */
+static int ftrace_modify_code(unsigned long pc, unsigned int old,
unsigned int new, bool validate)
+{
- unsigned int replaced;
u32 is a bit clearer for instructions.
Fix it.
- /*
* Note:
* Due to modules and __init, code can disappear and change,
* we need to protect against faulting as well as code changing.
* We do this by aarch64_insn_*() which use the probe_kernel_*().
*
* No lock is held here because all the modifications are run
* through stop_machine().
*/
- if (validate) {
if (aarch64_insn_read((void *)pc, &replaced))
return -EFAULT;
if (replaced != old)
return -EINVAL;
- }
- if (aarch64_insn_patch_text_nosync((void *)pc, new))
return -EPERM;
I think you're better off propagating the errors here, rather than overriding them with EFAULT/EINVAL/EPERM.
Steven explained this.
- return 0;
+}
+/*
- Replace tracer function in ftrace_caller()
- */
+int ftrace_update_ftrace_func(ftrace_func_t func) +{
- unsigned long pc;
- unsigned int new;
- pc = (unsigned long)&ftrace_call;
- new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true);
- return ftrace_modify_code(pc, 0, new, false);
+}
+/*
- Turn on the call to ftrace_caller() in instrumented function
- */
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{
- unsigned long pc = rec->ip;
- unsigned int old, new;
- old = aarch64_insn_gen_nop();
- new = aarch64_insn_gen_branch_imm(pc, addr, true);
- return ftrace_modify_code(pc, old, new, true);
+}
+/*
- Turn off the call to ftrace_caller() in instrumented function
- */
+int ftrace_make_nop(struct module *mod,
struct dyn_ftrace *rec, unsigned long addr)
+{
- unsigned long pc = rec->ip;
- unsigned int old, new;
- old = aarch64_insn_gen_branch_imm(pc, addr, true);
- new = aarch64_insn_gen_nop();
- return ftrace_modify_code(pc, old, new, true);
+}
+int __init ftrace_dyn_arch_init(void *data) +{
- *(unsigned long *)data = 0;
- return 0;
+} +#endif /* CONFIG_DYNAMIC_FTRACE */
- #ifdef CONFIG_FUNCTION_GRAPH_TRACER /*
- function_graph tracer expects ftrace_return_to_handler() to be called
@@ -61,4 +144,34 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } }
+#ifdef CONFIG_DYNAMIC_FTRACE +/*
- Turn on/off the call to ftrace_graph_caller() in ftrace_caller()
- depending on @enable.
- */
+static int ftrace_modify_graph_caller(bool enable) +{
- unsigned long pc = (unsigned long)&ftrace_graph_call;
- unsigned int branch, nop, old, new;
- branch = aarch64_insn_gen_branch_imm(pc,
(unsigned long)ftrace_graph_caller, false);
- nop = aarch64_insn_gen_nop();
- old = enable ? nop : branch;
- new = enable ? branch : nop;
- return ftrace_modify_code(pc, old, new, true);
You could rewrite this as:
if (enable) return ftrace_modify_code(pc, nop, branch, true); else return ftrace_modify_code(pc, branch, nop, true);
which I find easier to read.
Yep, fix it.
-Takahiro AKASHI
Will
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/ftrace.h | 13 ++++++++- arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..c44c4b1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,7 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); +extern void *return_address(unsigned int);
struct dyn_arch_ftrace { /* No extra data needed for arm64 */ @@ -33,6 +34,16 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) */ return addr; } -#endif /* __ASSEMBLY__ */ + +#define HAVE_ARCH_CALLER_ADDR + +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#endif /* ifndef __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index ac67fd0..b5bfa7f 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -7,12 +7,13 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_insn.o = -pg +CFLAGS_REMOVE_return_address.o = -pg
# Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ - hyp-stub.o psci.o cpu_ops.o insn.o + hyp-stub.o psci.o cpu_ops.o insn.o return_address.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 0000000..89102a6 --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/* + * arch/arm64/kernel/return_address.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/export.h> +#include <linux/ftrace.h> + +#include <asm/stacktrace.h> + +struct return_address_data { + unsigned int level; + void *addr; +}; + +static int save_return_addr(struct stackframe *frame, void *d) +{ + struct return_address_data *data = d; + + if (!data->level) { + data->addr = (void *)frame->pc; + return 1; + } else { + --data->level; + return 0; + } +} + +void *return_address(unsigned int level) +{ + struct return_address_data data; + struct stackframe frame; + register unsigned long current_sp asm ("sp"); + + data.level = level + 2; + data.addr = NULL; + + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.pc = (unsigned long)return_address; /* dummy */ + + walk_stackframe(&frame, save_return_addr, &data); + + if (!data.level) + return data.addr; + else + return NULL; +} +EXPORT_SYMBOL_GPL(return_address);
On Thu, Mar 13, 2014 at 10:13:49AM +0000, AKASHI Takahiro wrote:
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
How do you ensure that -fno-omit-frame-pointer is passed?
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/include/asm/ftrace.h | 13 ++++++++- arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..c44c4b1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,7 @@ #ifndef __ASSEMBLY__ extern void _mcount(unsigned long); +extern void *return_address(unsigned int); struct dyn_arch_ftrace { /* No extra data needed for arm64 */ @@ -33,6 +34,16 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) */ return addr; } -#endif /* __ASSEMBLY__ */
+#define HAVE_ARCH_CALLER_ADDR
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6))
Could we change the core definitions of these macros (in linux/ftrace.h) to use return_address, then provide an overridable version of return_address that defaults to __builtin_return_address, instead of copy-pasting this sequence?
diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 0000000..89102a6 --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/*
- arch/arm64/kernel/return_address.c
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/export.h> +#include <linux/ftrace.h>
+#include <asm/stacktrace.h>
+struct return_address_data {
- unsigned int level;
- void *addr;
+};
+static int save_return_addr(struct stackframe *frame, void *d) +{
- struct return_address_data *data = d;
- if (!data->level) {
data->addr = (void *)frame->pc;
return 1;
- } else {
--data->level;
return 0;
- }
+}
+void *return_address(unsigned int level) +{
- struct return_address_data data;
- struct stackframe frame;
- register unsigned long current_sp asm ("sp");
- data.level = level + 2;
- data.addr = NULL;
- frame.fp = (unsigned long)__builtin_frame_address(0);
- frame.sp = current_sp;
- frame.pc = (unsigned long)return_address; /* dummy */
- walk_stackframe(&frame, save_return_addr, &data);
- if (!data.level)
return data.addr;
- else
return NULL;
+} +EXPORT_SYMBOL_GPL(return_address);
This whole file is basically copied from arch/arm/, but it's not too much code. Ideally the toolchain would have made use of the frame pointer, but it looks like it doesn't bother.
Will
On Thu, 2014-03-13 at 15:54 +0000, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:49AM +0000, AKASHI Takahiro wrote:
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
How do you ensure that -fno-omit-frame-pointer is passed?
Perhaps -pg does the same thing?
+#define HAVE_ARCH_CALLER_ADDR
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6))
Could we change the core definitions of these macros (in linux/ftrace.h) to use return_address, then provide an overridable version of return_address that defaults to __builtin_return_address, instead of copy-pasting this sequence?
We could add a new macro:
/* All archs should have this, but we define it for consistency */ #ifndef ftrace_return_address0 # define ftrace_return_address0 __builtin_return_address(0) #endif /* Archs may use other ways for ADDR1 and beyond */ #ifndef ftrace_return_address # define ftrace_return_address(n) __builtin_return_address(n) #endif
And then have:
#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0) #define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1)) [...]
And then you would only need to redefine ftrace_return_address.
-- Steve
On 03/14/2014 03:07 AM, Steven Rostedt wrote:
On Thu, 2014-03-13 at 15:54 +0000, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:49AM +0000, AKASHI Takahiro wrote:
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
How do you ensure that -fno-omit-frame-pointer is passed?
Perhaps -pg does the same thing?
+#define HAVE_ARCH_CALLER_ADDR
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6))
Could we change the core definitions of these macros (in linux/ftrace.h) to use return_address, then provide an overridable version of return_address that defaults to __builtin_return_address, instead of copy-pasting this sequence?
We could add a new macro:
/* All archs should have this, but we define it for consistency */ #ifndef ftrace_return_address0 # define ftrace_return_address0 __builtin_return_address(0) #endif /* Archs may use other ways for ADDR1 and beyond */ #ifndef ftrace_return_address # define ftrace_return_address(n) __builtin_return_address(n) #endif
And then have:
#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0) #define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1)) [...]
And then you would only need to redefine ftrace_return_address.
I'm going to create a separate RFC, including fixes for other archs.
-Takahiro AKASHI
-- Steve
On 03/14/2014 12:54 AM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:49AM +0000, AKASHI Takahiro wrote:
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
How do you ensure that -fno-omit-frame-pointer is passed?
arm64 selects ARCH_WANT_FRAME_POINTERS, then FRAME_POINTER is on (lib/Kconfig.debug) and so -fno-omit-frame-pointer is appended (${TOP}/Makefile). (stacktrace.c also assumes FRAME_POINTER.)
Do you think I should remove the comment above?
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/include/asm/ftrace.h | 13 ++++++++- arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..c44c4b1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,7 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); +extern void *return_address(unsigned int);
struct dyn_arch_ftrace { /* No extra data needed for arm64 */ @@ -33,6 +34,16 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) */ return addr; } -#endif /* __ASSEMBLY__ */
+#define HAVE_ARCH_CALLER_ADDR
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6))
Could we change the core definitions of these macros (in linux/ftrace.h) to use return_address, then provide an overridable version of return_address that defaults to __builtin_return_address, instead of copy-pasting this sequence?
I think I understand what you mean, and will try to post a separate RFC, but I also want to hold off this change on this patch since such a change may raise a small controversy from other archs' maintainers.
diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 0000000..89102a6 --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/*
- arch/arm64/kernel/return_address.c
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/export.h> +#include <linux/ftrace.h>
+#include <asm/stacktrace.h>
+struct return_address_data {
- unsigned int level;
- void *addr;
+};
+static int save_return_addr(struct stackframe *frame, void *d) +{
- struct return_address_data *data = d;
- if (!data->level) {
data->addr = (void *)frame->pc;
return 1;
- } else {
--data->level;
return 0;
- }
+}
+void *return_address(unsigned int level) +{
- struct return_address_data data;
- struct stackframe frame;
- register unsigned long current_sp asm ("sp");
- data.level = level + 2;
- data.addr = NULL;
- frame.fp = (unsigned long)__builtin_frame_address(0);
- frame.sp = current_sp;
- frame.pc = (unsigned long)return_address; /* dummy */
- walk_stackframe(&frame, save_return_addr, &data);
- if (!data.level)
return data.addr;
- else
return NULL;
+} +EXPORT_SYMBOL_GPL(return_address);
This whole file is basically copied from arch/arm/, but it's not too much code. Ideally the toolchain would have made use of the frame pointer, but it looks like it doesn't bother.
I confirmed that __builtin_return_address([123456]) doesn't work even with -fno-omit-frame-pointer. Keep this as it is.
-Takahiro AKASHI
Will
On Fri, Mar 14, 2014 at 03:00:14AM +0000, AKASHI Takahiro wrote:
On 03/14/2014 12:54 AM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:49AM +0000, AKASHI Takahiro wrote:
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
How do you ensure that -fno-omit-frame-pointer is passed?
arm64 selects ARCH_WANT_FRAME_POINTERS, then FRAME_POINTER is on (lib/Kconfig.debug) and so -fno-omit-frame-pointer is appended (${TOP}/Makefile). (stacktrace.c also assumes FRAME_POINTER.)
Do you think I should remove the comment above?
Yes please, it sounds like everything is taken care of automatically, so there's no need to scare people in the commit message!
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..c44c4b1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,7 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); +extern void *return_address(unsigned int);
struct dyn_arch_ftrace { /* No extra data needed for arm64 */ @@ -33,6 +34,16 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) */ return addr; } -#endif /* __ASSEMBLY__ */
+#define HAVE_ARCH_CALLER_ADDR
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6))
Could we change the core definitions of these macros (in linux/ftrace.h) to use return_address, then provide an overridable version of return_address that defaults to __builtin_return_address, instead of copy-pasting this sequence?
I think I understand what you mean, and will try to post a separate RFC, but I also want to hold off this change on this patch since such a change may raise a small controversy from other archs' maintainers.
I don't see anything controversial here, but ok. Steve already posted something you can get started with.
Will
On 03/14/2014 07:09 PM, Will Deacon wrote:
On Fri, Mar 14, 2014 at 03:00:14AM +0000, AKASHI Takahiro wrote:
On 03/14/2014 12:54 AM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:49AM +0000, AKASHI Takahiro wrote:
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Please note that this implementation assumes that we have frame pointers. (which means kernel should be compiled with -fno-omit-frame-pointer.)
How do you ensure that -fno-omit-frame-pointer is passed?
arm64 selects ARCH_WANT_FRAME_POINTERS, then FRAME_POINTER is on (lib/Kconfig.debug) and so -fno-omit-frame-pointer is appended (${TOP}/Makefile). (stacktrace.c also assumes FRAME_POINTER.)
Do you think I should remove the comment above?
Yes please, it sounds like everything is taken care of automatically, so there's no need to scare people in the commit message!
OK, I will do so.
Thanks, -Takahiro AKASHI
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..c44c4b1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,7 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); +extern void *return_address(unsigned int);
struct dyn_arch_ftrace { /* No extra data needed for arm64 */ @@ -33,6 +34,16 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) */ return addr; } -#endif /* __ASSEMBLY__ */
+#define HAVE_ARCH_CALLER_ADDR
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6))
Could we change the core definitions of these macros (in linux/ftrace.h) to use return_address, then provide an overridable version of return_address that defaults to __builtin_return_address, instead of copy-pasting this sequence?
I think I understand what you mean, and will try to post a separate RFC, but I also want to hold off this change on this patch since such a change may raise a small controversy from other archs' maintainers.
I don't see anything controversial here, but ok. Steve already posted something you can get started with.
Will
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Please note that we can't trace compat system calls here because AArch32 mode does not share the same syscall table with AArch64. Just define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS in order to avoid unexpected results (bogus syscalls reported or even hang-up).
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 20 ++++++++++++++++ arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 48 ++++++++++++++++++++++---------------- 5 files changed, 52 insertions(+), 20 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6954959..b1dcdb4 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -43,6 +43,7 @@ config ARM64 select HAVE_MEMBLOCK select HAVE_PATA_PLATFORM select HAVE_PERF_EVENTS + select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index c44c4b1..4ef06f1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -44,6 +44,26 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) #define CALLER_ADDR4 ((unsigned long)return_address(4)) #define CALLER_ADDR5 ((unsigned long)return_address(5)) #define CALLER_ADDR6 ((unsigned long)return_address(6)) + +#include <asm/compat.h> + +/* + * Because AArch32 mode does not share the same syscall table with AArch64, + * tracing compat syscalls may result in reporting bogus syscalls or even + * hang-up, so just do not trace them. + * See kernel/trace/trace_syscalls.c + * + * x86 code says: + * If the user realy wants these, then they should use the + * raw syscall tracepoints with filtering. + */ +#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS 1 +static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) +{ + if (is_compat_task()) + return true; + return false; +} #endif /* ifndef __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@
#include <linux/err.h>
+extern const void *sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h> + +#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 9993a8f..9c52b3e 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -41,6 +41,9 @@ #include <asm/traps.h> #include <asm/system_misc.h>
+#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> + /* * TODO: does not yet catch signals sent when the child dies. * in exit.c or in signal.c. @@ -1062,29 +1065,31 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) { unsigned long saved_reg;
- if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return regs->syscallno; + if (test_thread_flag(TIF_SYSCALL_TRACE)) { + /* + * A scrach register (ip(r12) on AArch32, x7 on AArch64) is + * used to denote syscall entry/exit: + * 0 -> entry + */ + if (is_compat_task()) { + saved_reg = regs->regs[12]; + regs->regs[12] = 0; + } else { + saved_reg = regs->regs[7]; + regs->regs[7] = 0; + }
- /* - * A scrach register (ip(r12) on AArch32, x7 on AArch64) is - * used to denote syscall entry/exit: - * 0 -> entry - */ - if (is_compat_task()) { - saved_reg = regs->regs[12]; - regs->regs[12] = 0; - } else { - saved_reg = regs->regs[7]; - regs->regs[7] = 0; - } + if (tracehook_report_syscall_entry(regs)) + regs->syscallno = ~0UL;
- if (tracehook_report_syscall_entry(regs)) - regs->syscallno = ~0UL; + if (is_compat_task()) + regs->regs[12] = saved_reg; + else + regs->regs[7] = saved_reg; + }
- if (is_compat_task()) - regs->regs[12] = saved_reg; - else - regs->regs[7] = saved_reg; + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_enter(regs, regs->syscallno);
return regs->syscallno; } @@ -1093,6 +1098,9 @@ asmlinkage void syscall_trace_exit(struct pt_regs *regs) { unsigned long saved_reg;
+ if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_exit(regs, regs_return_value(regs)); + if (!test_thread_flag(TIF_SYSCALL_TRACE)) return;
On Thu, Mar 13, 2014 at 10:13:50AM +0000, AKASHI Takahiro wrote:
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Please note that we can't trace compat system calls here because AArch32 mode does not share the same syscall table with AArch64. Just define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS in order to avoid unexpected results (bogus syscalls reported or even hang-up).
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 20 ++++++++++++++++ arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 48 ++++++++++++++++++++++---------------- 5 files changed, 52 insertions(+), 20 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6954959..b1dcdb4 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -43,6 +43,7 @@ config ARM64 select HAVE_MEMBLOCK select HAVE_PATA_PLATFORM select HAVE_PERF_EVENTS
- select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index c44c4b1..4ef06f1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -44,6 +44,26 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) #define CALLER_ADDR4 ((unsigned long)return_address(4)) #define CALLER_ADDR5 ((unsigned long)return_address(5)) #define CALLER_ADDR6 ((unsigned long)return_address(6))
+#include <asm/compat.h>
+/*
- Because AArch32 mode does not share the same syscall table with AArch64,
- tracing compat syscalls may result in reporting bogus syscalls or even
- hang-up, so just do not trace them.
- See kernel/trace/trace_syscalls.c
- x86 code says:
- If the user realy wants these, then they should use the
- raw syscall tracepoints with filtering.
Fair enough.
- */
+#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS 1
You don't need the '1' here.
+static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) +{
if (is_compat_task())
return true;
return false;
+}
return is_compat_task();
#endif /* ifndef __ASSEMBLY__ */ #endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@ #include <linux/err.h> +extern const void *sys_call_table[]; static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h>
+#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 9993a8f..9c52b3e 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -41,6 +41,9 @@ #include <asm/traps.h> #include <asm/system_misc.h> +#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h>
/*
- TODO: does not yet catch signals sent when the child dies.
- in exit.c or in signal.c.
@@ -1062,29 +1065,31 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) { unsigned long saved_reg;
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
return regs->syscallno;
- if (test_thread_flag(TIF_SYSCALL_TRACE)) {
/*
* A scrach register (ip(r12) on AArch32, x7 on AArch64) is
* used to denote syscall entry/exit:
* 0 -> entry
*/
if (is_compat_task()) {
if (arch_trace_is_compat_syscall())
With those changes:
Acked-by: Will Deacon will.deacon@arm.com
Will
On 03/14/2014 01:25 AM, Will Deacon wrote:
On Thu, Mar 13, 2014 at 10:13:50AM +0000, AKASHI Takahiro wrote:
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Please note that we can't trace compat system calls here because AArch32 mode does not share the same syscall table with AArch64. Just define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS in order to avoid unexpected results (bogus syscalls reported or even hang-up).
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 20 ++++++++++++++++ arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 48 ++++++++++++++++++++++---------------- 5 files changed, 52 insertions(+), 20 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6954959..b1dcdb4 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -43,6 +43,7 @@ config ARM64 select HAVE_MEMBLOCK select HAVE_PATA_PLATFORM select HAVE_PERF_EVENTS
- select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index c44c4b1..4ef06f1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -44,6 +44,26 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) #define CALLER_ADDR4 ((unsigned long)return_address(4)) #define CALLER_ADDR5 ((unsigned long)return_address(5)) #define CALLER_ADDR6 ((unsigned long)return_address(6))
+#include <asm/compat.h>
+/*
- Because AArch32 mode does not share the same syscall table with AArch64,
- tracing compat syscalls may result in reporting bogus syscalls or even
- hang-up, so just do not trace them.
- See kernel/trace/trace_syscalls.c
- x86 code says:
- If the user realy wants these, then they should use the
- raw syscall tracepoints with filtering.
Fair enough.
- */
+#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS 1
You don't need the '1' here.
OK.
+static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) +{
if (is_compat_task())
return true;
return false;
+}
return is_compat_task();
Fix it.
#endif /* ifndef __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@
#include <linux/err.h>
+extern const void *sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h>
+#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 9993a8f..9c52b3e 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -41,6 +41,9 @@ #include <asm/traps.h> #include <asm/system_misc.h>
+#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h>
- /*
- TODO: does not yet catch signals sent when the child dies.
- in exit.c or in signal.c.
@@ -1062,29 +1065,31 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) { unsigned long saved_reg;
- if (!test_thread_flag(TIF_SYSCALL_TRACE))
return regs->syscallno;
- if (test_thread_flag(TIF_SYSCALL_TRACE)) {
/*
* A scrach register (ip(r12) on AArch32, x7 on AArch64) is
* used to denote syscall entry/exit:
* 0 -> entry
*/
if (is_compat_task()) {
if (arch_trace_is_compat_syscall())
I don't mind either way, but this part of code comes from the original syscall_trace() (ie. ptrace stuff), and has nothing to do with ftrace events. (You know, arch_trace_is_compat_syscall() is currently defined in asm/ftrace.h.) So I'd like to keep it unchanged unless you really want.
With those changes:
Acked-by: Will Deacon will.deacon@arm.com
Thank you, -Takahiro AKASHI
Will
This patchset implements a function tracer on arm64. There was another implementation from Cavium network, but both of us agreed to use my patchset as future base. He is supposed to review this code, too.
The only issue that I had some concern on was "fault protection" code in prepare_ftrace_return(). With discussions with Steven and Tim (as author of arm ftrace), I removed that code since I'm not quite sure about possibility of "fault" occurrences in this function.
The code is tested on ARMv8 Fast Model with the following tracers & events: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint (but only for AArch64 tasks) irqsoff & preemptirqsoff (which use CALLER_ADDRx) and also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Prerequisites are: * "arm64: make a single hook to syscall_trace() for all syscall features" patch * "arm64: split syscall_trace() into separate functions for enter/exit" patch * "arm64: Add regs_return_value() in syscall.h" patch * "arm64: is_compat_task is defined both in asm/compat.h and linux/compat.h" patch
Please be careful: * Patch [3/7] gets warnings from checkpatch, but they are based on the original's coding style. * Patch [7/7] may conflict with my audit patch because both changes the same location in syscall_trace_enter/exit(). I expect the functions are called in this order: On entry, * tracehook_report_syscall(ENTER) * trace_sys_enter() * audit_syscall_entry() On exit, * audit_sysscall_exit() * trace_sys_exit() * tracehook_report_syscall(EXIT)
Changes from v6 to v7: * changed to use gpfx variable instead of defining altmcount in recordmcount.c [3/7] * declared return_to_handler using ENTRY/END macros [4/7] * changed to use u32 instead of int as instruction words, and simplified ftrace_modify_graph_caller() [5/7] * simplified arch_trace_is_compat_call() for readability [7/7] * added the following patch to prerequisite list, "arm64: is_compat_task is defined both in asm/compat.h and linux/compat.h" and changed to use linux/compat.h instead of asm/compat.h in asm/ftrace.h to avoid compile errors against some files (ie. do_mounts.c and etc) if FTRACE & !COMPAT. [7/7]
Changes from v5 to v6: * changed the order of patches to avoid any bisect error (I have not tried though) * added EM_AARCH64 and R_AARCH64_ABS64 definitions in scripts/recordmcount.c just in case elf.h on host machine doesn't have them. [3/7] * updated a frame pointer (x29) in _mcount() to make it look like a normal function [4/7] * aligned with the patch, "arm64: split syscall_trace() into separate functions for enter/exit" [7/7] * defined ARCH_TRACE_IGNORE_COMPAT_SYSCALLS in order not to trace compat syscalls [7/7]
Chnages from v4 to v5: * improved the description of stack layout [1/7] * aligned with the change in "arm64: make a single hook to syscall_trace() for all syscall features" v3 [5/7]
Changes from v3 to v4: * removed unnecessary "#ifdef" [1,2/7] * changed stack depth from 48B to 16B in mcount()/ftrace_caller() (a bug) [1/7] * changed MCOUNT_INSN_SIZE to AARCH64_INSN_SIZE [1,7/7] * added a guard againt TIF_SYSCALL_TRACEPOINT [5/7] * corrected the second argument passed to trace_sys_exit() (a bug) [5/7] * aligned with the change in "arm64: make a single hook to syscall_trace() for all syscall features" v2 [5/7]
Changes from v2 to v3: * optimized register usages in asm (by not saving x0, x1, and x2) * removed "fault protection" code in prepare_ftrace_return() * rewrote ftrace_modify_code() using "hotpatch" interfaces * revised descriptions in comments
Changes from v1 to v2: * splitted one patch into some pieces for easier review (especially function tracer + dynamic ftrace + CALLER_ADDRx) * put return_address() in a separate file * renamed __mcount to _mcount (it was my mistake) * changed stackframe handling to get parent's frame pointer * removed ARCH_SUPPORTS_FTRACE_OPS * switched to "hotpatch" interfaces from Huawai * revised descriptions in comments
AKASHI Takahiro (7): arm64: add __ASSEMBLY__ in asm/insn.h arm64: Add 'notrace' attribute to unwind_frame() for ftrace ftrace: Add arm64 support to recordmcount arm64: Add ftrace support arm64: ftrace: Add dynamic ftrace support arm64: ftrace: Add CALLER_ADDRx macros arm64: ftrace: Add system call tracepoint
arch/arm64/Kconfig | 6 + arch/arm64/include/asm/ftrace.h | 67 ++++++++++++ arch/arm64/include/asm/insn.h | 2 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 + arch/arm64/kernel/Makefile | 7 +- arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 218 +++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 178 ++++++++++++++++++++++++++++++ arch/arm64/kernel/ptrace.c | 9 ++ arch/arm64/kernel/return_address.c | 55 ++++++++++ arch/arm64/kernel/stacktrace.c | 2 +- scripts/recordmcount.c | 7 ++ scripts/recordmcount.pl | 5 + 14 files changed, 561 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c create mode 100644 arch/arm64/kernel/return_address.c
Since insn.h is indirectly included in asm/entry-ftrace.S, we need to exclude some declarations by __ASSEMBLY__.
Acked-by: Will Deacon will.deacon@arm.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/insn.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index c44ad39..dc1f73b 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -21,6 +21,7 @@ /* A64 instructions are always 32 bits. */ #define AARCH64_INSN_SIZE 4
+#ifndef __ASSEMBLY__ /* * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a * Section C3.1 "A64 instruction index by encoding": @@ -104,5 +105,6 @@ bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); int aarch64_insn_patch_text_nosync(void *addr, u32 insn); int aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt); int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt); +#endif /* __ASSEMBLY__ */
#endif /* __ASM_INSN_H */
walk_stackframe() calls unwind_frame(), and if walk_stackframe() is "notrace", unwind_frame() should be also "notrace".
Acked-by: Will Deacon will.deacon@arm.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index c3b6c63..54122c4 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -35,7 +35,7 @@ * ldp x29, x30, [sp] * add sp, sp, #0x10 */ -int unwind_frame(struct stackframe *frame) +int notrace unwind_frame(struct stackframe *frame) { unsigned long high, low; unsigned long fp = frame->fp;
Recordmcount utility under scripts is run, after compiling each object, to find out all the locations of calling _mcount() and put them into specific seciton named __mcount_loc. Then linker collects all such information into a table in the kernel image (between __start_mcount_loc and __stop_mcount_loc) for later use by ftrace.
This patch adds arm64 specific definitions to identify such locations. There are two types of implementation, C and Perl. On arm64, only C version is used to build the kernel now that CONFIG_HAVE_C_RECORDMCOUNT is on. But Perl version is also maintained.
This patch also contains a workaround just in case where a header file, elf.h, on host machine doesn't have definitions of EM_AARCH64 nor R_AARCH64_ABS64. Without them, compiling C version of recordmcount will fail.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 ++ scripts/recordmcount.c | 7 +++++++ scripts/recordmcount.pl | 5 +++++ 3 files changed, 14 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 27bbcfc..340e344 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -27,12 +27,14 @@ config ARM64 select HARDIRQS_SW_RESEND select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_TRACEHOOK + select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FTRACE_MCOUNT_RECORD select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 9c22317..e11aa4a 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -40,6 +40,11 @@ #define R_METAG_NONE 3 #endif
+#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#define R_AARCH64_ABS64 257 +#endif + static int fd_map; /* File descriptor for file being modified. */ static int mmap_failed; /* Boolean flag. */ static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */ @@ -347,6 +352,8 @@ do_file(char const *const fname) case EM_ARM: reltype = R_ARM_ABS32; altmcount = "__gnu_mcount_nc"; break; + case EM_AARCH64: + reltype = R_AARCH64_ABS64; gpfx = '_'; break; case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; case EM_METAG: reltype = R_METAG_ADDR32; altmcount = "_mcount_wrapper"; diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 91280b8..397b6b8 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$";
+} elsif ($arch eq "arm64") { + $alignment = 3; + $section_type = '%progbits'; + $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$"; + $type = ".quad"; } elsif ($arch eq "ia64") { $mcount_regex = "^\s*([0-9a-fA-F]+):.*\s_mcount$"; $type = "data8";
On Sat, Mar 15, 2014 at 05:40:44AM +0000, AKASHI Takahiro wrote:
Recordmcount utility under scripts is run, after compiling each object, to find out all the locations of calling _mcount() and put them into specific seciton named __mcount_loc. Then linker collects all such information into a table in the kernel image (between __start_mcount_loc and __stop_mcount_loc) for later use by ftrace.
This patch adds arm64 specific definitions to identify such locations. There are two types of implementation, C and Perl. On arm64, only C version is used to build the kernel now that CONFIG_HAVE_C_RECORDMCOUNT is on. But Perl version is also maintained.
This patch also contains a workaround just in case where a header file, elf.h, on host machine doesn't have definitions of EM_AARCH64 nor R_AARCH64_ABS64. Without them, compiling C version of recordmcount will fail.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Acked-by: Will Deacon will.deacon@arm.com
Will
This patchset implements a function tracer on arm64. There was another implementation from Cavium network, but both of us agreed to use my patchset as future base. He is supposed to review this code, too.
The only issue that I had some concern on was "fault protection" code in prepare_ftrace_return(). With discussions with Steven and Tim (as author of arm ftrace), I removed that code since I'm not quite sure about possibility of "fault" occurrences in this function.
The code is tested on ARMv8 Fast Model with the following tracers & events: function tracer with dynamic ftrace function graph tracer with dynamic ftrace syscall tracepoint (but only for AArch64 tasks) irqsoff & preemptirqsoff (which use CALLER_ADDRx) and also verified with in-kernel tests, FTRACE_SELFTEST, FTRACE_STARTUP_TEST and EVENT_TRACE_TEST_SYSCALLS.
Prerequisites are: * "arm64: make a single hook to syscall_trace() for all syscall features" patch * "arm64: split syscall_trace() into separate functions for enter/exit" patch * "arm64: Add regs_return_value() in syscall.h" patch * "arm64: is_compat_task is defined both in asm/compat.h and linux/compat.h" patch
Please be careful: * Patch [3/7] gets warnings from checkpatch, but they are based on the original's coding style. * Patch [7/7] may conflict with my audit patch because both changes the same location in syscall_trace_enter/exit(). I expect the functions are called in this order: On entry, * tracehook_report_syscall(ENTER) * trace_sys_enter() * audit_syscall_entry() On exit, * audit_sysscall_exit() * trace_sys_exit() * tracehook_report_syscall(EXIT)
Changes from v7 to v8: * rebased on 3.15-rc * untabified the ascii art in entry-ftrace.S [5/8] * added patch [1/8] in order to make CALLER_ADDRx() macro more generic across most architectures [1,7/8]
Changes from v6 to v7: * changed to use gpfx variable instead of defining altmcount in recordmcount.c [3/7] * declared return_to_handler using ENTRY/END macros [4/7] * changed to use u32 instead of int as instruction words, and simplified ftrace_modify_graph_caller() [5/7] * simplified arch_trace_is_compat_call() for readability [7/7] * added the following patch to prerequisite list, "arm64: is_compat_task is defined both in asm/compat.h and linux/compat.h" and changed to use linux/compat.h instead of asm/compat.h in asm/ftrace.h to avoid compile errors against some files (ie. do_mounts.c and etc) if FTRACE & !COMPAT. [7/7]
Changes from v5 to v6: * changed the order of patches to avoid any bisect error (I have not tried though) * added EM_AARCH64 and R_AARCH64_ABS64 definitions in scripts/recordmcount.c just in case elf.h on host machine doesn't have them. [3/7] * updated a frame pointer (x29) in _mcount() to make it look like a normal function [4/7] * aligned with the patch, "arm64: split syscall_trace() into separate functions for enter/exit" [7/7] * defined ARCH_TRACE_IGNORE_COMPAT_SYSCALLS in order not to trace compat syscalls [7/7]
Chnages from v4 to v5: * improved the description of stack layout [1/7] * aligned with the change in "arm64: make a single hook to syscall_trace() for all syscall features" v3 [5/7]
Changes from v3 to v4: * removed unnecessary "#ifdef" [1,2/7] * changed stack depth from 48B to 16B in mcount()/ftrace_caller() (a bug) [1/7] * changed MCOUNT_INSN_SIZE to AARCH64_INSN_SIZE [1,7/7] * added a guard againt TIF_SYSCALL_TRACEPOINT [5/7] * corrected the second argument passed to trace_sys_exit() (a bug) [5/7] * aligned with the change in "arm64: make a single hook to syscall_trace() for all syscall features" v2 [5/7]
Changes from v2 to v3: * optimized register usages in asm (by not saving x0, x1, and x2) * removed "fault protection" code in prepare_ftrace_return() * rewrote ftrace_modify_code() using "hotpatch" interfaces * revised descriptions in comments
Changes from v1 to v2: * splitted one patch into some pieces for easier review (especially function tracer + dynamic ftrace + CALLER_ADDRx) * put return_address() in a separate file * renamed __mcount to _mcount (it was my mistake) * changed stackframe handling to get parent's frame pointer * removed ARCH_SUPPORTS_FTRACE_OPS * switched to "hotpatch" interfaces from Huawai * revised descriptions in comments
AKASHI Takahiro (7): arm64: add __ASSEMBLY__ in asm/insn.h arm64: Add 'notrace' attribute to unwind_frame() for ftrace ftrace: Add arm64 support to recordmcount arm64: Add ftrace support arm64: ftrace: Add dynamic ftrace support arm64: ftrace: Add CALLER_ADDRx macros arm64: ftrace: Add system call tracepoint
AKASHI Takahiro (8): ftrace: make CALLER_ADDRx macros more generic arm64: add __ASSEMBLY__ in asm/insn.h arm64: Add 'notrace' attribute to unwind_frame() for ftrace ftrace: Add arm64 support to recordmcount arm64: Add ftrace support arm64: ftrace: Add dynamic ftrace support arm64: ftrace: Add CALLER_ADDRx macros arm64: ftrace: Add system call tracepoint
arch/arm/include/asm/ftrace.h | 10 +- arch/arm64/Kconfig | 6 + arch/arm64/include/asm/ftrace.h | 59 ++++++++++ arch/arm64/include/asm/insn.h | 2 + arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 + arch/arm64/kernel/Makefile | 7 +- arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 218 ++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 176 +++++++++++++++++++++++++++++ arch/arm64/kernel/ptrace.c | 9 ++ arch/arm64/kernel/return_address.c | 55 +++++++++ arch/arm64/kernel/stacktrace.c | 2 +- arch/blackfin/include/asm/ftrace.h | 11 +- arch/parisc/include/asm/ftrace.h | 10 +- arch/sh/include/asm/ftrace.h | 10 +- arch/xtensa/include/asm/ftrace.h | 14 +-- include/linux/ftrace.h | 34 +++--- scripts/recordmcount.c | 7 ++ scripts/recordmcount.pl | 5 + 20 files changed, 577 insertions(+), 65 deletions(-) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c create mode 100644 arch/arm64/kernel/return_address.c
Most archs with HAVE_ARCH_CALLER_ADDR have the almost same definitions of CALLER_ADDRx(n), and so put them into linux/ftrace.h.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm/include/asm/ftrace.h | 10 +--------- arch/blackfin/include/asm/ftrace.h | 11 +---------- arch/parisc/include/asm/ftrace.h | 10 +--------- arch/sh/include/asm/ftrace.h | 10 +--------- arch/xtensa/include/asm/ftrace.h | 14 ++++---------- include/linux/ftrace.h | 34 ++++++++++++++++++---------------- 6 files changed, 26 insertions(+), 63 deletions(-)
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index f89515a..eb577f4 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,15 +52,7 @@ extern inline void *return_address(unsigned int level)
#endif
-#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_addr(n) return_address(n)
#endif /* ifndef __ASSEMBLY__ */
diff --git a/arch/blackfin/include/asm/ftrace.h b/arch/blackfin/include/asm/ftrace.h index 8a02950..2f1c3c2 100644 --- a/arch/blackfin/include/asm/ftrace.h +++ b/arch/blackfin/include/asm/ftrace.h @@ -66,16 +66,7 @@ extern inline void *return_address(unsigned int level)
#endif /* CONFIG_FRAME_POINTER */
-#define HAVE_ARCH_CALLER_ADDR - -/* inline function or macro may lead to unexpected result */ -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h index 72c0faf..544ed8e 100644 --- a/arch/parisc/include/asm/ftrace.h +++ b/arch/parisc/include/asm/ftrace.h @@ -24,15 +24,7 @@ extern void return_to_handler(void);
extern unsigned long return_address(unsigned int);
-#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#define CALLER_ADDR4 return_address(4) -#define CALLER_ADDR5 return_address(5) -#define CALLER_ADDR6 return_address(6) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/sh/include/asm/ftrace.h b/arch/sh/include/asm/ftrace.h index 13e9966..e79fb6e 100644 --- a/arch/sh/include/asm/ftrace.h +++ b/arch/sh/include/asm/ftrace.h @@ -40,15 +40,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) /* arch/sh/kernel/return_address.c */ extern void *return_address(unsigned int);
-#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 736b9d2..6c6d9a9 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -12,24 +12,18 @@
#include <asm/processor.h>
-#define HAVE_ARCH_CALLER_ADDR #ifndef __ASSEMBLY__ -#define CALLER_ADDR0 ({ unsigned long a0, a1; \ +#define ftrace_return_address0 ({ unsigned long a0, a1; \ __asm__ __volatile__ ( \ "mov %0, a0\n" \ "mov %1, a1\n" \ : "=r"(a0), "=r"(a1)); \ MAKE_PC_FROM_RA(a0, a1); }) + #ifdef CONFIG_FRAME_POINTER extern unsigned long return_address(unsigned level); -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#else /* CONFIG_FRAME_POINTER */ -#define CALLER_ADDR1 (0) -#define CALLER_ADDR2 (0) -#define CALLER_ADDR3 (0) -#endif /* CONFIG_FRAME_POINTER */ +#define ftrace_return_address(n) return_address(n) +#endif #endif /* __ASSEMBLY__ */
#ifdef CONFIG_FUNCTION_TRACER diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 9212b01..18f1ae7 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -614,25 +614,27 @@ static inline void __ftrace_enabled_restore(int enabled) #endif }
-#ifndef HAVE_ARCH_CALLER_ADDR +/* All archs should have this, but we define it for consistency */ +#ifndef ftrace_return_address0 +# define ftrace_return_address0 __builtin_return_address(0) +#endif + +/* Archs may use other ways for ADDR1 and beyond */ +#ifndef ftrace_return_address # ifdef CONFIG_FRAME_POINTER -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) -# define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) -# define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3)) -# define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4)) -# define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5)) -# define CALLER_ADDR6 ((unsigned long)__builtin_return_address(6)) +# define ftrace_return_address(n) __builtin_return_address(n) # else -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 0UL -# define CALLER_ADDR2 0UL -# define CALLER_ADDR3 0UL -# define CALLER_ADDR4 0UL -# define CALLER_ADDR5 0UL -# define CALLER_ADDR6 0UL +# define ftrace_return_address(n) 0UL # endif -#endif /* ifndef HAVE_ARCH_CALLER_ADDR */ +#endif + +#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0) +#define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1)) +#define CALLER_ADDR2 ((unsigned long)ftrace_return_address(2)) +#define CALLER_ADDR3 ((unsigned long)ftrace_return_address(3)) +#define CALLER_ADDR4 ((unsigned long)ftrace_return_address(4)) +#define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5)) +#define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6))
#ifdef CONFIG_IRQSOFF_TRACER extern void time_hardirqs_on(unsigned long a0, unsigned long a1);
Hi Akashi,
On Wed, Apr 30, 2014 at 10:54:29AM +0100, AKASHI Takahiro wrote:
Most archs with HAVE_ARCH_CALLER_ADDR have the almost same definitions of CALLER_ADDRx(n), and so put them into linux/ftrace.h.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm/include/asm/ftrace.h | 10 +--------- arch/blackfin/include/asm/ftrace.h | 11 +---------- arch/parisc/include/asm/ftrace.h | 10 +--------- arch/sh/include/asm/ftrace.h | 10 +--------- arch/xtensa/include/asm/ftrace.h | 14 ++++---------- include/linux/ftrace.h | 34 ++++++++++++++++++---------------- 6 files changed, 26 insertions(+), 63 deletions(-)
This one looks a bit too widespread to be merged via the arm64 tree. I wonder if the ftrace maintainers would consider taking it as a cleanup?
Will
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index f89515a..eb577f4 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,15 +52,7 @@ extern inline void *return_address(unsigned int level) #endif -#define HAVE_ARCH_CALLER_ADDR
-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_addr(n) return_address(n) #endif /* ifndef __ASSEMBLY__ */ diff --git a/arch/blackfin/include/asm/ftrace.h b/arch/blackfin/include/asm/ftrace.h index 8a02950..2f1c3c2 100644 --- a/arch/blackfin/include/asm/ftrace.h +++ b/arch/blackfin/include/asm/ftrace.h @@ -66,16 +66,7 @@ extern inline void *return_address(unsigned int level) #endif /* CONFIG_FRAME_POINTER */ -#define HAVE_ARCH_CALLER_ADDR
-/* inline function or macro may lead to unexpected result */ -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n) #endif /* __ASSEMBLY__ */ diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h index 72c0faf..544ed8e 100644 --- a/arch/parisc/include/asm/ftrace.h +++ b/arch/parisc/include/asm/ftrace.h @@ -24,15 +24,7 @@ extern void return_to_handler(void); extern unsigned long return_address(unsigned int); -#define HAVE_ARCH_CALLER_ADDR
-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#define CALLER_ADDR4 return_address(4) -#define CALLER_ADDR5 return_address(5) -#define CALLER_ADDR6 return_address(6) +#define ftrace_return_address(n) return_address(n) #endif /* __ASSEMBLY__ */ diff --git a/arch/sh/include/asm/ftrace.h b/arch/sh/include/asm/ftrace.h index 13e9966..e79fb6e 100644 --- a/arch/sh/include/asm/ftrace.h +++ b/arch/sh/include/asm/ftrace.h @@ -40,15 +40,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) /* arch/sh/kernel/return_address.c */ extern void *return_address(unsigned int); -#define HAVE_ARCH_CALLER_ADDR
-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n) #endif /* __ASSEMBLY__ */ diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 736b9d2..6c6d9a9 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -12,24 +12,18 @@ #include <asm/processor.h> -#define HAVE_ARCH_CALLER_ADDR #ifndef __ASSEMBLY__ -#define CALLER_ADDR0 ({ unsigned long a0, a1; \ +#define ftrace_return_address0 ({ unsigned long a0, a1; \ __asm__ __volatile__ ( \ "mov %0, a0\n" \ "mov %1, a1\n" \ : "=r"(a0), "=r"(a1)); \ MAKE_PC_FROM_RA(a0, a1); })
#ifdef CONFIG_FRAME_POINTER extern unsigned long return_address(unsigned level); -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#else /* CONFIG_FRAME_POINTER */ -#define CALLER_ADDR1 (0) -#define CALLER_ADDR2 (0) -#define CALLER_ADDR3 (0) -#endif /* CONFIG_FRAME_POINTER */ +#define ftrace_return_address(n) return_address(n) +#endif #endif /* __ASSEMBLY__ */ #ifdef CONFIG_FUNCTION_TRACER diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 9212b01..18f1ae7 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -614,25 +614,27 @@ static inline void __ftrace_enabled_restore(int enabled) #endif } -#ifndef HAVE_ARCH_CALLER_ADDR +/* All archs should have this, but we define it for consistency */ +#ifndef ftrace_return_address0 +# define ftrace_return_address0 __builtin_return_address(0) +#endif
+/* Archs may use other ways for ADDR1 and beyond */ +#ifndef ftrace_return_address # ifdef CONFIG_FRAME_POINTER -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) -# define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) -# define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3)) -# define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4)) -# define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5)) -# define CALLER_ADDR6 ((unsigned long)__builtin_return_address(6)) +# define ftrace_return_address(n) __builtin_return_address(n) # else -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 0UL -# define CALLER_ADDR2 0UL -# define CALLER_ADDR3 0UL -# define CALLER_ADDR4 0UL -# define CALLER_ADDR5 0UL -# define CALLER_ADDR6 0UL +# define ftrace_return_address(n) 0UL # endif -#endif /* ifndef HAVE_ARCH_CALLER_ADDR */ +#endif
+#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0) +#define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1)) +#define CALLER_ADDR2 ((unsigned long)ftrace_return_address(2)) +#define CALLER_ADDR3 ((unsigned long)ftrace_return_address(3)) +#define CALLER_ADDR4 ((unsigned long)ftrace_return_address(4)) +#define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5)) +#define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6)) #ifdef CONFIG_IRQSOFF_TRACER extern void time_hardirqs_on(unsigned long a0, unsigned long a1); -- 1.7.9.5
On Fri, 2 May 2014 19:13:48 +0100 Will Deacon will.deacon@arm.com wrote:
Hi Akashi,
On Wed, Apr 30, 2014 at 10:54:29AM +0100, AKASHI Takahiro wrote:
Most archs with HAVE_ARCH_CALLER_ADDR have the almost same definitions of CALLER_ADDRx(n), and so put them into linux/ftrace.h.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm/include/asm/ftrace.h | 10 +--------- arch/blackfin/include/asm/ftrace.h | 11 +---------- arch/parisc/include/asm/ftrace.h | 10 +--------- arch/sh/include/asm/ftrace.h | 10 +--------- arch/xtensa/include/asm/ftrace.h | 14 ++++---------- include/linux/ftrace.h | 34 ++++++++++++++++++---------------- 6 files changed, 26 insertions(+), 63 deletions(-)
This one looks a bit too widespread to be merged via the arm64 tree. I wonder if the ftrace maintainers would consider taking it as a cleanup?
I actually was the one to recommend this approach. But I have some small comments to the patch. I'll reply directly to the patch with them.
-- Steve
On Fri, 2 May 2014 19:13:48 +0100 Will Deacon will.deacon@arm.com wrote:
This one looks a bit too widespread to be merged via the arm64 tree. I wonder if the ftrace maintainers would consider taking it as a cleanup?
Oh, and I can take the patch if you feel uncomfortable with taking something so spread out. As it deals with ftrace I probably can get away with a cross the board arch change to it.
-- Steve
On Wed, 30 Apr 2014 18:54:29 +0900 AKASHI Takahiro takahiro.akashi@linaro.org wrote:
Most archs with HAVE_ARCH_CALLER_ADDR have the almost same definitions of CALLER_ADDRx(n), and so put them into linux/ftrace.h.
Please add a bit more to the change log. Like what you did. Something like:
---- Most archs with HAVE_ARCH_CALLER_ADDR have pretty much the same definitions of CALLER_ADDRx(n). Instead of duplicating the code for all the archs, define a ftrace_return_address0() and ftrace_return_address(n) that can be overwritten by the archs if they need to do something different. Instead of 7 macros in every arch, we now only have at most 2 (and actually only 1 as ftrace_return_address0() should be the same for all archs).
The CALLER_ADDRx(n) will now be defined in linux/ftrace.h and use the ftrace_return_address*(n?) macros. This removes a lot of the duplicate code. -----
Use that if you want :-)
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm/include/asm/ftrace.h | 10 +--------- arch/blackfin/include/asm/ftrace.h | 11 +---------- arch/parisc/include/asm/ftrace.h | 10 +--------- arch/sh/include/asm/ftrace.h | 10 +--------- arch/xtensa/include/asm/ftrace.h | 14 ++++---------- include/linux/ftrace.h | 34 ++++++++++++++++++---------------- 6 files changed, 26 insertions(+), 63 deletions(-)
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index f89515a..eb577f4 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,15 +52,7 @@ extern inline void *return_address(unsigned int level) #endif -#define HAVE_ARCH_CALLER_ADDR
-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_addr(n) return_address(n) #endif /* ifndef __ASSEMBLY__ */ diff --git a/arch/blackfin/include/asm/ftrace.h b/arch/blackfin/include/asm/ftrace.h index 8a02950..2f1c3c2 100644 --- a/arch/blackfin/include/asm/ftrace.h +++ b/arch/blackfin/include/asm/ftrace.h @@ -66,16 +66,7 @@ extern inline void *return_address(unsigned int level) #endif /* CONFIG_FRAME_POINTER */ -#define HAVE_ARCH_CALLER_ADDR
-/* inline function or macro may lead to unexpected result */ -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n) #endif /* __ASSEMBLY__ */ diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h index 72c0faf..544ed8e 100644 --- a/arch/parisc/include/asm/ftrace.h +++ b/arch/parisc/include/asm/ftrace.h @@ -24,15 +24,7 @@ extern void return_to_handler(void); extern unsigned long return_address(unsigned int); -#define HAVE_ARCH_CALLER_ADDR
-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#define CALLER_ADDR4 return_address(4) -#define CALLER_ADDR5 return_address(5) -#define CALLER_ADDR6 return_address(6) +#define ftrace_return_address(n) return_address(n) #endif /* __ASSEMBLY__ */ diff --git a/arch/sh/include/asm/ftrace.h b/arch/sh/include/asm/ftrace.h index 13e9966..e79fb6e 100644 --- a/arch/sh/include/asm/ftrace.h +++ b/arch/sh/include/asm/ftrace.h @@ -40,15 +40,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) /* arch/sh/kernel/return_address.c */ extern void *return_address(unsigned int); -#define HAVE_ARCH_CALLER_ADDR
-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n) #endif /* __ASSEMBLY__ */ diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 736b9d2..6c6d9a9 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -12,24 +12,18 @@ #include <asm/processor.h> -#define HAVE_ARCH_CALLER_ADDR #ifndef __ASSEMBLY__ -#define CALLER_ADDR0 ({ unsigned long a0, a1; \ +#define ftrace_return_address0 ({ unsigned long a0, a1; \ __asm__ __volatile__ ( \ "mov %0, a0\n" \ "mov %1, a1\n" \ : "=r"(a0), "=r"(a1)); \ MAKE_PC_FROM_RA(a0, a1); })
#ifdef CONFIG_FRAME_POINTER extern unsigned long return_address(unsigned level); -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#else /* CONFIG_FRAME_POINTER */ -#define CALLER_ADDR1 (0) -#define CALLER_ADDR2 (0) -#define CALLER_ADDR3 (0) -#endif /* CONFIG_FRAME_POINTER */ +#define ftrace_return_address(n) return_address(n)
I would add a comment here that states:
/* * #else !CONFIG_FRAME_POINTER * * Define CALLER_ADDRn (n > 0) to 0 is the default in linux/ftrace.h if * ftrace_return_address(n) and CONFIG_FRAME_POINTER are not defined. */
Otherwise it looks like you are missing the #else statement.
+#endif #endif /* __ASSEMBLY__ */ #ifdef CONFIG_FUNCTION_TRACER diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 9212b01..18f1ae7 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -614,25 +614,27 @@ static inline void __ftrace_enabled_restore(int enabled) #endif } -#ifndef HAVE_ARCH_CALLER_ADDR +/* All archs should have this, but we define it for consistency */
The comment is a little confusing. I think it may be better stated as:
/* * All archs should be able to use __builtin_return_address(0) but * we allow them to redefine it for consistency. */
+#ifndef ftrace_return_address0 +# define ftrace_return_address0 __builtin_return_address(0) +#endif
+/* Archs may use other ways for ADDR1 and beyond */
How about:
/* Not all archs can use __builtin_return_address(n) where n > 0 */
+#ifndef ftrace_return_address # ifdef CONFIG_FRAME_POINTER -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) -# define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) -# define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3)) -# define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4)) -# define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5)) -# define CALLER_ADDR6 ((unsigned long)__builtin_return_address(6)) +# define ftrace_return_address(n) __builtin_return_address(n) # else -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 0UL -# define CALLER_ADDR2 0UL -# define CALLER_ADDR3 0UL -# define CALLER_ADDR4 0UL -# define CALLER_ADDR5 0UL -# define CALLER_ADDR6 0UL +# define ftrace_return_address(n) 0UL # endif -#endif /* ifndef HAVE_ARCH_CALLER_ADDR */ +#endif
+#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0) +#define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1)) +#define CALLER_ADDR2 ((unsigned long)ftrace_return_address(2)) +#define CALLER_ADDR3 ((unsigned long)ftrace_return_address(3)) +#define CALLER_ADDR4 ((unsigned long)ftrace_return_address(4)) +#define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5)) +#define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6))
Other than that, it looks good. You can send me the patch and I'll add it to my 3.16 queue.
Feel free to reply to this email with a v9. When I pull patches into git, it adds the link to the thread for the patch in LKML. To keep this entire thread, just reply to it.
Thanks!
-- Steve
#ifdef CONFIG_IRQSOFF_TRACER extern void time_hardirqs_on(unsigned long a0, unsigned long a1);
Hi guys,
On Fri, May 02, 2014 at 08:19:57PM +0100, Steven Rostedt wrote:
On Wed, 30 Apr 2014 18:54:29 +0900 AKASHI Takahiro takahiro.akashi@linaro.org wrote:
Most archs with HAVE_ARCH_CALLER_ADDR have the almost same definitions of CALLER_ADDRx(n), and so put them into linux/ftrace.h.
Please add a bit more to the change log. Like what you did. Something like:
Most archs with HAVE_ARCH_CALLER_ADDR have pretty much the same definitions of CALLER_ADDRx(n). Instead of duplicating the code for all the archs, define a ftrace_return_address0() and ftrace_return_address(n) that can be overwritten by the archs if they need to do something different. Instead of 7 macros in every arch, we now only have at most 2 (and actually only 1 as ftrace_return_address0() should be the same for all archs).
The CALLER_ADDRx(n) will now be defined in linux/ftrace.h and use the ftrace_return_address*(n?) macros. This removes a lot of the duplicate code.
Use that if you want :-)
Akashi: did you get around to posting a new version of this patch? We can't take your arm64 patches until you get the core stuff merged...
Will
On Mon, 12 May 2014 16:58:11 +0100 Will Deacon will.deacon@arm.com wrote:
Akashi: did you get around to posting a new version of this patch? We can't take your arm64 patches until you get the core stuff merged...
I haven't seen any patches yet.
What I can also do is to create a separate branch based on mainline, and just apply this change to the core. Then you could pull that branch with a note to Linus that it is also in my tree with the common ancestor.
If he pulls your work first, he'll only get that change from my tree, and if he pulls mine first it doesn't matter as all the changes will be there.
This is something he said at Kernel Summit that was normal procedure if there's a change in one tree that another tree is dependent on.
-- Steve
Hey Steve,
On Mon, May 12, 2014 at 05:05:35PM +0100, Steven Rostedt wrote:
On Mon, 12 May 2014 16:58:11 +0100 Will Deacon will.deacon@arm.com wrote:
Akashi: did you get around to posting a new version of this patch? We can't take your arm64 patches until you get the core stuff merged...
I haven't seen any patches yet.
What I can also do is to create a separate branch based on mainline, and just apply this change to the core. Then you could pull that branch with a note to Linus that it is also in my tree with the common ancestor.
If he pulls your work first, he'll only get that change from my tree, and if he pulls mine first it doesn't matter as all the changes will be there.
This is something he said at Kernel Summit that was normal procedure if there's a change in one tree that another tree is dependent on.
That sounds great, and nicely solves the dependency between our trees for 3.16 at the same time.
If you point us at the branch and promise not to rebase it, that would be fantastic.
Cheers,
Will
On Mon, 12 May 2014 17:12:46 +0100 Will Deacon will.deacon@arm.com wrote:
If you point us at the branch and promise not to rebase it, that would be fantastic.
I will when I get the patch(es) :-) and run it through all my tests.
-- Steve
Hi Will, Steven,
So sorry, I completely missed this message thread. I will submit a new patch (replacement of [1/8]) in the following e-mail.
-Takahiro AKASHI
On 05/03/2014 04:19 AM, Steven Rostedt wrote:
On Wed, 30 Apr 2014 18:54:29 +0900 AKASHI Takahiro takahiro.akashi@linaro.org wrote:
Most archs with HAVE_ARCH_CALLER_ADDR have the almost same definitions of CALLER_ADDRx(n), and so put them into linux/ftrace.h.
Please add a bit more to the change log. Like what you did. Something like:
Most archs with HAVE_ARCH_CALLER_ADDR have pretty much the same definitions of CALLER_ADDRx(n). Instead of duplicating the code for all the archs, define a ftrace_return_address0() and ftrace_return_address(n) that can be overwritten by the archs if they need to do something different. Instead of 7 macros in every arch, we now only have at most 2 (and actually only 1 as ftrace_return_address0() should be the same for all archs).
The CALLER_ADDRx(n) will now be defined in linux/ftrace.h and use the ftrace_return_address*(n?) macros. This removes a lot of the duplicate code.
Use that if you want :-)
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm/include/asm/ftrace.h | 10 +--------- arch/blackfin/include/asm/ftrace.h | 11 +---------- arch/parisc/include/asm/ftrace.h | 10 +--------- arch/sh/include/asm/ftrace.h | 10 +--------- arch/xtensa/include/asm/ftrace.h | 14 ++++---------- include/linux/ftrace.h | 34 ++++++++++++++++++---------------- 6 files changed, 26 insertions(+), 63 deletions(-)
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index f89515a..eb577f4 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,15 +52,7 @@ extern inline void *return_address(unsigned int level)
#endif
-#define HAVE_ARCH_CALLER_ADDR
-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_addr(n) return_address(n)
#endif /* ifndef __ASSEMBLY__ */
diff --git a/arch/blackfin/include/asm/ftrace.h b/arch/blackfin/include/asm/ftrace.h index 8a02950..2f1c3c2 100644 --- a/arch/blackfin/include/asm/ftrace.h +++ b/arch/blackfin/include/asm/ftrace.h @@ -66,16 +66,7 @@ extern inline void *return_address(unsigned int level)
#endif /* CONFIG_FRAME_POINTER */
-#define HAVE_ARCH_CALLER_ADDR
-/* inline function or macro may lead to unexpected result */ -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h index 72c0faf..544ed8e 100644 --- a/arch/parisc/include/asm/ftrace.h +++ b/arch/parisc/include/asm/ftrace.h @@ -24,15 +24,7 @@ extern void return_to_handler(void);
extern unsigned long return_address(unsigned int);
-#define HAVE_ARCH_CALLER_ADDR
-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#define CALLER_ADDR4 return_address(4) -#define CALLER_ADDR5 return_address(5) -#define CALLER_ADDR6 return_address(6) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/sh/include/asm/ftrace.h b/arch/sh/include/asm/ftrace.h index 13e9966..e79fb6e 100644 --- a/arch/sh/include/asm/ftrace.h +++ b/arch/sh/include/asm/ftrace.h @@ -40,15 +40,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) /* arch/sh/kernel/return_address.c */ extern void *return_address(unsigned int);
-#define HAVE_ARCH_CALLER_ADDR
-#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 736b9d2..6c6d9a9 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -12,24 +12,18 @@
#include <asm/processor.h>
-#define HAVE_ARCH_CALLER_ADDR #ifndef __ASSEMBLY__ -#define CALLER_ADDR0 ({ unsigned long a0, a1; \ +#define ftrace_return_address0 ({ unsigned long a0, a1; \ __asm__ __volatile__ ( \ "mov %0, a0\n" \ "mov %1, a1\n" \ : "=r"(a0), "=r"(a1)); \ MAKE_PC_FROM_RA(a0, a1); })
- #ifdef CONFIG_FRAME_POINTER extern unsigned long return_address(unsigned level);
-#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#else /* CONFIG_FRAME_POINTER */ -#define CALLER_ADDR1 (0) -#define CALLER_ADDR2 (0) -#define CALLER_ADDR3 (0) -#endif /* CONFIG_FRAME_POINTER */ +#define ftrace_return_address(n) return_address(n)
I would add a comment here that states:
/*
- #else !CONFIG_FRAME_POINTER
- Define CALLER_ADDRn (n > 0) to 0 is the default in linux/ftrace.h if
- ftrace_return_address(n) and CONFIG_FRAME_POINTER are not defined.
*/
Otherwise it looks like you are missing the #else statement.
+#endif #endif /* __ASSEMBLY__ */
#ifdef CONFIG_FUNCTION_TRACER diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 9212b01..18f1ae7 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -614,25 +614,27 @@ static inline void __ftrace_enabled_restore(int enabled) #endif }
-#ifndef HAVE_ARCH_CALLER_ADDR +/* All archs should have this, but we define it for consistency */
The comment is a little confusing. I think it may be better stated as:
/*
- All archs should be able to use __builtin_return_address(0) but
- we allow them to redefine it for consistency.
*/
+#ifndef ftrace_return_address0 +# define ftrace_return_address0 __builtin_return_address(0) +#endif
+/* Archs may use other ways for ADDR1 and beyond */
How about:
/* Not all archs can use __builtin_return_address(n) where n > 0 */
+#ifndef ftrace_return_address # ifdef CONFIG_FRAME_POINTER -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) -# define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) -# define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3)) -# define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4)) -# define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5)) -# define CALLER_ADDR6 ((unsigned long)__builtin_return_address(6)) +# define ftrace_return_address(n) __builtin_return_address(n) # else -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 0UL -# define CALLER_ADDR2 0UL -# define CALLER_ADDR3 0UL -# define CALLER_ADDR4 0UL -# define CALLER_ADDR5 0UL -# define CALLER_ADDR6 0UL +# define ftrace_return_address(n) 0UL # endif -#endif /* ifndef HAVE_ARCH_CALLER_ADDR */ +#endif
+#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0) +#define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1)) +#define CALLER_ADDR2 ((unsigned long)ftrace_return_address(2)) +#define CALLER_ADDR3 ((unsigned long)ftrace_return_address(3)) +#define CALLER_ADDR4 ((unsigned long)ftrace_return_address(4)) +#define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5)) +#define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6))
Other than that, it looks good. You can send me the patch and I'll add it to my 3.16 queue.
Feel free to reply to this email with a v9. When I pull patches into git, it adds the link to the thread for the patch in LKML. To keep this entire thread, just reply to it.
Thanks!
-- Steve
#ifdef CONFIG_IRQSOFF_TRACER extern void time_hardirqs_on(unsigned long a0, unsigned long a1);
Most archs with HAVE_ARCH_CALLER_ADDR have pretty much the same definitions of CALLER_ADDRx(n). Instead of duplicating the code for all the archs, define a ftrace_return_address0() and ftrace_return_address(n) that can be overwritten by the archs if they need to do something different. Instead of 7 macros in every arch, we now only have at most 2 (and actually only 1 as ftrace_return_address0() should be the same for all archs).
The CALLER_ADDRx(n) will now be defined in linux/ftrace.h and use the ftrace_return_address*(n?) macros. This removes a lot of the duplicate code.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm/include/asm/ftrace.h | 10 +--------- arch/blackfin/include/asm/ftrace.h | 11 +---------- arch/parisc/include/asm/ftrace.h | 10 +--------- arch/sh/include/asm/ftrace.h | 10 +--------- arch/xtensa/include/asm/ftrace.h | 14 ++++---------- include/linux/ftrace.h | 34 ++++++++++++++++++---------------- 6 files changed, 26 insertions(+), 63 deletions(-)
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index f89515a..eb577f4 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,15 +52,7 @@ extern inline void *return_address(unsigned int level)
#endif
-#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_addr(n) return_address(n)
#endif /* ifndef __ASSEMBLY__ */
diff --git a/arch/blackfin/include/asm/ftrace.h b/arch/blackfin/include/asm/ftrace.h index 8a02950..2f1c3c2 100644 --- a/arch/blackfin/include/asm/ftrace.h +++ b/arch/blackfin/include/asm/ftrace.h @@ -66,16 +66,7 @@ extern inline void *return_address(unsigned int level)
#endif /* CONFIG_FRAME_POINTER */
-#define HAVE_ARCH_CALLER_ADDR - -/* inline function or macro may lead to unexpected result */ -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h index 72c0faf..544ed8e 100644 --- a/arch/parisc/include/asm/ftrace.h +++ b/arch/parisc/include/asm/ftrace.h @@ -24,15 +24,7 @@ extern void return_to_handler(void);
extern unsigned long return_address(unsigned int);
-#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#define CALLER_ADDR4 return_address(4) -#define CALLER_ADDR5 return_address(5) -#define CALLER_ADDR6 return_address(6) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/sh/include/asm/ftrace.h b/arch/sh/include/asm/ftrace.h index 13e9966..e79fb6e 100644 --- a/arch/sh/include/asm/ftrace.h +++ b/arch/sh/include/asm/ftrace.h @@ -40,15 +40,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) /* arch/sh/kernel/return_address.c */ extern void *return_address(unsigned int);
-#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 736b9d2..6c6d9a9 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -12,24 +12,18 @@
#include <asm/processor.h>
-#define HAVE_ARCH_CALLER_ADDR #ifndef __ASSEMBLY__ -#define CALLER_ADDR0 ({ unsigned long a0, a1; \ +#define ftrace_return_address0 ({ unsigned long a0, a1; \ __asm__ __volatile__ ( \ "mov %0, a0\n" \ "mov %1, a1\n" \ : "=r"(a0), "=r"(a1)); \ MAKE_PC_FROM_RA(a0, a1); }) + #ifdef CONFIG_FRAME_POINTER extern unsigned long return_address(unsigned level); -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#else /* CONFIG_FRAME_POINTER */ -#define CALLER_ADDR1 (0) -#define CALLER_ADDR2 (0) -#define CALLER_ADDR3 (0) -#endif /* CONFIG_FRAME_POINTER */ +#define ftrace_return_address(n) return_address(n) +#endif #endif /* __ASSEMBLY__ */
#ifdef CONFIG_FUNCTION_TRACER diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 9212b01..18f1ae7 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -614,25 +614,27 @@ static inline void __ftrace_enabled_restore(int enabled) #endif }
-#ifndef HAVE_ARCH_CALLER_ADDR +/* All archs should have this, but we define it for consistency */ +#ifndef ftrace_return_address0 +# define ftrace_return_address0 __builtin_return_address(0) +#endif + +/* Archs may use other ways for ADDR1 and beyond */ +#ifndef ftrace_return_address # ifdef CONFIG_FRAME_POINTER -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) -# define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) -# define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3)) -# define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4)) -# define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5)) -# define CALLER_ADDR6 ((unsigned long)__builtin_return_address(6)) +# define ftrace_return_address(n) __builtin_return_address(n) # else -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 0UL -# define CALLER_ADDR2 0UL -# define CALLER_ADDR3 0UL -# define CALLER_ADDR4 0UL -# define CALLER_ADDR5 0UL -# define CALLER_ADDR6 0UL +# define ftrace_return_address(n) 0UL # endif -#endif /* ifndef HAVE_ARCH_CALLER_ADDR */ +#endif + +#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0) +#define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1)) +#define CALLER_ADDR2 ((unsigned long)ftrace_return_address(2)) +#define CALLER_ADDR3 ((unsigned long)ftrace_return_address(3)) +#define CALLER_ADDR4 ((unsigned long)ftrace_return_address(4)) +#define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5)) +#define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6))
#ifdef CONFIG_IRQSOFF_TRACER extern void time_hardirqs_on(unsigned long a0, unsigned long a1);
On Wed, Apr 30, 2014 at 11:54 AM, AKASHI Takahiro takahiro.akashi@linaro.org wrote:
Most archs with HAVE_ARCH_CALLER_ADDR have the almost same definitions of CALLER_ADDRx(n), and so put them into linux/ftrace.h.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
On arm (at least shmobile_defconfig and versatile_defconfig) with gcc 4.6.3:
kernel/sched/core.c: In function 'get_parent_ip': kernel/sched/core.c:2520:10: warning: unsupported argument to '__builtin_return_address' [enabled by default] kernel/sched/core.c:2522:11: warning: unsupported argument to '__builtin_return_address' [enabled by default]
With gcc 4.9.0:
kernel/sched/core.c: In function 'get_parent_ip': include/linux/ftrace.h:627:36: warning: unsupported argument to '__builtin_return_address' # define ftrace_return_address(n) __builtin_return_address(n) ^ include/linux/ftrace.h:635:38: note: in expansion of macro 'ftrace_return_address' #define CALLER_ADDR2 ((unsigned long)ftrace_return_address(2)) ^ kernel/sched/core.c:2520:10: note: in expansion of macro 'CALLER_ADDR2' addr = CALLER_ADDR2; ^ include/linux/ftrace.h:627:36: warning: unsupported argument to '__builtin_return_address' # define ftrace_return_address(n) __builtin_return_address(n) ^ include/linux/ftrace.h:636:38: note: in expansion of macro 'ftrace_return_address' #define CALLER_ADDR3 ((unsigned long)ftrace_return_address(3)) ^ kernel/sched/core.c:2522:11: note: in expansion of macro 'CALLER_ADDR3' addr = CALLER_ADDR3; ^
Gr{oetje,eeting}s,
Geert
-- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
The clean up of CALLER_ADDR*() functions required the archs to either use the default __builtin_return_address(X) (where X > 0) or override it with something the arch can use. To override it, the arch would define function_return_address(x).
The arm architecture requires this to be redefined but instead of defining function_return_address(x) it defined function_return_addr(x).
Reported-by: Geert Uytterhoeven geert@linux-m68k.org Signed-off-by: Steven Rostedt rostedt@goodmis.org ---- diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index eb577f4..39eb16b 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,7 +52,7 @@ extern inline void *return_address(unsigned int level)
#endif
-#define ftrace_return_addr(n) return_address(n) +#define ftrace_return_address(n) return_address(n)
#endif /* ifndef __ASSEMBLY__ */
Hi Steven,
On Wed, Jun 11, 2014 at 3:23 PM, Steven Rostedt rostedt@goodmis.org wrote:
The clean up of CALLER_ADDR*() functions required the archs to either use the default __builtin_return_address(X) (where X > 0) or override it with something the arch can use. To override it, the arch would define function_return_address(x).
ftrace_return_address(x)
The arm architecture requires this to be redefined but instead of defining function_return_address(x) it defined function_return_addr(x).
ftrace_return_address(x) ... ftrace_return_address(x)
Reported-by: Geert Uytterhoeven geert@linux-m68k.org Signed-off-by: Steven Rostedt rostedt@goodmis.org
Nevertheless, your patch kills the warnings. Thanks!
Tested-by: Geert Uytterhoeven geert@linux-m68k.org
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index eb577f4..39eb16b 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,7 +52,7 @@ extern inline void *return_address(unsigned int level)
#endif
-#define ftrace_return_addr(n) return_address(n) +#define ftrace_return_address(n) return_address(n)
#endif /* ifndef __ASSEMBLY__ */
Gr{oetje,eeting}s,
Geert
-- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
On Wed, 11 Jun 2014 15:38:49 +0200 Geert Uytterhoeven geert@linux-m68k.org wrote:
Hi Steven,
On Wed, Jun 11, 2014 at 3:23 PM, Steven Rostedt rostedt@goodmis.org wrote:
The clean up of CALLER_ADDR*() functions required the archs to either use the default __builtin_return_address(X) (where X > 0) or override it with something the arch can use. To override it, the arch would define function_return_address(x).
ftrace_return_address(x)
The arm architecture requires this to be redefined but instead of defining function_return_address(x) it defined function_return_addr(x).
ftrace_return_address(x) ... ftrace_return_address(x)
As long as I got the patch part right ;-)
Will, please update the change log with the correct name. Including the subject. Thanks!
-- Steve
Reported-by: Geert Uytterhoeven geert@linux-m68k.org Signed-off-by: Steven Rostedt rostedt@goodmis.org
Nevertheless, your patch kills the warnings. Thanks!
Tested-by: Geert Uytterhoeven geert@linux-m68k.org
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index eb577f4..39eb16b 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,7 +52,7 @@ extern inline void *return_address(unsigned int level)
#endif
-#define ftrace_return_addr(n) return_address(n) +#define ftrace_return_address(n) return_address(n)
#endif /* ifndef __ASSEMBLY__ */
Gr{oetje,eeting}s,
Geert
-- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Sorry for my silly typo, and thank you, Steve, for your fix.
-Takahiro AKASHI
On 11 June 2014 22:44, Steven Rostedt rostedt@goodmis.org wrote:
On Wed, 11 Jun 2014 15:38:49 +0200 Geert Uytterhoeven geert@linux-m68k.org wrote:
Hi Steven,
On Wed, Jun 11, 2014 at 3:23 PM, Steven Rostedt rostedt@goodmis.org
wrote:
The clean up of CALLER_ADDR*() functions required the archs to either use the default __builtin_return_address(X) (where X > 0) or override it with something the arch can use. To override it, the arch would define function_return_address(x).
ftrace_return_address(x)
The arm architecture requires this to be redefined but instead of defining function_return_address(x) it defined function_return_addr(x).
ftrace_return_address(x) ... ftrace_return_address(x)
As long as I got the patch part right ;-)
Will, please update the change log with the correct name. Including the subject. Thanks!
-- Steve
Reported-by: Geert Uytterhoeven geert@linux-m68k.org Signed-off-by: Steven Rostedt rostedt@goodmis.org
Nevertheless, your patch kills the warnings. Thanks!
Tested-by: Geert Uytterhoeven geert@linux-m68k.org
diff --git a/arch/arm/include/asm/ftrace.h
b/arch/arm/include/asm/ftrace.h
index eb577f4..39eb16b 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,7 +52,7 @@ extern inline void *return_address(unsigned int
level)
#endif
-#define ftrace_return_addr(n) return_address(n) +#define ftrace_return_address(n) return_address(n)
#endif /* ifndef __ASSEMBLY__ */
Gr{oetje,eeting}s,
Geert
-- Geert Uytterhoeven -- There's lots of Linux beyond ia32 --
geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker.
But
when I'm talking to journalists I just say "programmer" or something
like that.
-- Linus Torvalds
Since insn.h is indirectly included in asm/entry-ftrace.S, we need to exclude some declarations by __ASSEMBLY__.
Acked-by: Will Deacon will.deacon@arm.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/insn.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index c44ad39..dc1f73b 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -21,6 +21,7 @@ /* A64 instructions are always 32 bits. */ #define AARCH64_INSN_SIZE 4
+#ifndef __ASSEMBLY__ /* * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a * Section C3.1 "A64 instruction index by encoding": @@ -104,5 +105,6 @@ bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); int aarch64_insn_patch_text_nosync(void *addr, u32 insn); int aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt); int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt); +#endif /* __ASSEMBLY__ */
#endif /* __ASM_INSN_H */
walk_stackframe() calls unwind_frame(), and if walk_stackframe() is "notrace", unwind_frame() should be also "notrace".
Acked-by: Will Deacon will.deacon@arm.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index 38f0558..55437ba 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -35,7 +35,7 @@ * ldp x29, x30, [sp] * add sp, sp, #0x10 */ -int unwind_frame(struct stackframe *frame) +int notrace unwind_frame(struct stackframe *frame) { unsigned long high, low; unsigned long fp = frame->fp;
Recordmcount utility under scripts is run, after compiling each object, to find out all the locations of calling _mcount() and put them into specific seciton named __mcount_loc. Then linker collects all such information into a table in the kernel image (between __start_mcount_loc and __stop_mcount_loc) for later use by ftrace.
This patch adds arm64 specific definitions to identify such locations. There are two types of implementation, C and Perl. On arm64, only C version is used to build the kernel now that CONFIG_HAVE_C_RECORDMCOUNT is on. But Perl version is also maintained.
This patch also contains a workaround just in case where a header file, elf.h, on host machine doesn't have definitions of EM_AARCH64 nor R_AARCH64_ABS64. Without them, compiling C version of recordmcount will fail.
Acked-by: Will Deacon will.deacon@arm.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 ++ scripts/recordmcount.c | 7 +++++++ scripts/recordmcount.pl | 5 +++++ 3 files changed, 14 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e6e4d37..f467285 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -30,12 +30,14 @@ config ARM64 select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_KGDB select HAVE_ARCH_TRACEHOOK + select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS + select HAVE_FTRACE_MCOUNT_RECORD select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 9c22317..e11aa4a 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -40,6 +40,11 @@ #define R_METAG_NONE 3 #endif
+#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#define R_AARCH64_ABS64 257 +#endif + static int fd_map; /* File descriptor for file being modified. */ static int mmap_failed; /* Boolean flag. */ static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */ @@ -347,6 +352,8 @@ do_file(char const *const fname) case EM_ARM: reltype = R_ARM_ABS32; altmcount = "__gnu_mcount_nc"; break; + case EM_AARCH64: + reltype = R_AARCH64_ABS64; gpfx = '_'; break; case EM_IA_64: reltype = R_IA64_IMM64; gpfx = '_'; break; case EM_METAG: reltype = R_METAG_ADDR32; altmcount = "_mcount_wrapper"; diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 91280b8..397b6b8 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -279,6 +279,11 @@ if ($arch eq "x86_64") { $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_ARM_(CALL|PC24|THM_CALL)" . "\s+(__gnu_mcount_nc|mcount)$";
+} elsif ($arch eq "arm64") { + $alignment = 3; + $section_type = '%progbits'; + $mcount_regex = "^\s*([0-9a-fA-F]+):\s*R_AARCH64_CALL26\s+_mcount$"; + $type = ".quad"; } elsif ($arch eq "ia64") { $mcount_regex = "^\s*([0-9a-fA-F]+):.*\s_mcount$"; $type = "data8";
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Reviewed-by: Ganapatrao Kulkarni ganapatrao.kulkarni@cavium.com Acked-by: Will Deacon will.deacon@arm.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/ftrace.h | 23 +++++ arch/arm64/kernel/Makefile | 4 + arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 175 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 64 ++++++++++++++ 6 files changed, 272 insertions(+) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index f467285..856007e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -38,6 +38,8 @@ config ARM64 select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FTRACE_MCOUNT_RECORD + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..58ea595 --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/* + * arch/arm64/include/asm/ftrace.h + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H + +#include <asm/insn.h> + +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE + +#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 7d811d9..1ebcb4c 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,9 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg + # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ @@ -13,6 +16,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o +arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..b2d8c45 --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,175 @@ +/* + * arch/arm64/kernel/entry-ftrace.S + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +/* + * Gcc with -pg will put the following code in the beginning of each function: + * mov x0, x30 + * bl _mcount + * [function's body ...] + * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic + * ftrace is enabled. + * + * Please note that x0 as an argument will not be used here because we can + * get lr(x30) of instrumented function at any time by winding up call stack + * as long as the kernel is compiled without -fomit-frame-pointer. + * (or CONFIG_FRAME_POINTER, this is forced on arm64) + * + * stack layout after mcount_enter in _mcount(): + * + * current sp/fp => 0:+-----+ + * in _mcount() | x29 | -> instrumented function's fp + * +-----+ + * | x30 | -> _mcount()'s lr (= instrumented function's pc) + * old sp => +16:+-----+ + * when instrumented | | + * function calls | ... | + * _mcount() | | + * | | + * instrumented => +xx:+-----+ + * function's fp | x29 | -> parent's fp + * +-----+ + * | x30 | -> instrumented function's lr (= parent's pc) + * +-----+ + * | ... | + */ + + .macro mcount_enter + stp x29, x30, [sp, #-16]! + mov x29, sp + .endm + + .macro mcount_exit + ldp x29, x30, [sp], #16 + ret + .endm + + .macro mcount_adjust_addr rd, rn + sub \rd, \rn, #AARCH64_INSN_SIZE + .endm + + /* for instrumented function's parent */ + .macro mcount_get_parent_fp reg + ldr \reg, [x29] + ldr \reg, [\reg] + .endm + + /* for instrumented function */ + .macro mcount_get_pc0 reg + mcount_adjust_addr \reg, x30 + .endm + + .macro mcount_get_pc reg + ldr \reg, [x29, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr reg + ldr \reg, [x29] + ldr \reg, [\reg, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr_addr reg + ldr \reg, [x29] + add \reg, \reg, #8 + .endm + +/* + * void _mcount(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function makes calls, if enabled, to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST + ldr x0, =ftrace_trace_stop + ldr x0, [x0] // if ftrace_trace_stop + ret // return; +#endif + mcount_enter + + ldr x0, =ftrace_trace_function + ldr x2, [x0] + adr x0, ftrace_stub + cmp x0, x2 // if (ftrace_trace_function + b.eq skip_ftrace_call // != ftrace_stub) { + + mcount_get_pc x0 // function's pc + mcount_get_lr x1 // function's lr (= parent's pc) + blr x2 // (*ftrace_trace_function)(pc, lr); + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call: // return; + mcount_exit // } +#else + mcount_exit // return; + // } +skip_ftrace_call: + ldr x1, =ftrace_graph_return + ldr x2, [x1] // if ((ftrace_graph_return + cmp x0, x2 // != ftrace_stub) + b.ne ftrace_graph_caller + + ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry + ldr x2, [x1] // != ftrace_graph_entry_stub)) + ldr x0, =ftrace_graph_entry_stub + cmp x0, x2 + b.ne ftrace_graph_caller // ftrace_graph_caller(); + + mcount_exit +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount) + +ENTRY(ftrace_stub) + ret +ENDPROC(ftrace_stub) + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * void ftrace_graph_caller(void) + * + * Called from _mcount() or ftrace_caller() when function_graph tracer is + * selected. + * This function w/ prepare_ftrace_return() fakes link register's value on + * the call stack in order to intercept instrumented function's return path + * and run return_to_handler() later on its exit. + */ +ENTRY(ftrace_graph_caller) + mcount_get_lr_addr x0 // pointer to function's saved lr + mcount_get_pc x1 // function's pc + mcount_get_parent_fp x2 // parent's fp + bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp) + + mcount_exit +ENDPROC(ftrace_graph_caller) + +/* + * void return_to_handler(void) + * + * Run ftrace_return_to_handler() before going back to parent. + * @fp is checked against the value passed by ftrace_graph_caller() + * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled. + */ +ENTRY(return_to_handler) + str x0, [sp, #-16]! + mov x0, x29 // parent's fp + bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); + mov x30, x0 // restore the original return address + ldr x0, [sp], #16 + ret +END(return_to_handler) +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/* + * arch/arm64/kernel/ftrace.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * function_graph tracer expects ftrace_return_to_handler() to be called + * on the way back to parent. For this purpose, this function is called + * in _mcount() or ftrace_caller() to replace return address (*parent) on + * the call stack to return_to_handler. + * + * Note that @frame_pointer is used only for sanity check later. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + unsigned long frame_pointer) +{ + unsigned long return_hooker = (unsigned long)&return_to_handler; + unsigned long old; + struct ftrace_graph_ent trace; + int err; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + + /* + * Note: + * No protection against faulting at *parent, which may be seen + * on other archs. It's unlikely on AArch64. + */ + old = *parent; + *parent = return_hooker; + + trace.func = self_addr; + trace.depth = current->curr_ret_stack + 1; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + *parent = old; + return; + } + + err = ftrace_push_return_trace(old, self_addr, &trace.depth, + frame_pointer); + if (err == -EBUSY) { + *parent = old; + return; + } +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
On Wed, Apr 30, 2014 at 06:54:33PM +0900, AKASHI Takahiro wrote:
+/*
- arch/arm64/kernel/entry-ftrace.S
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h>
asm/insn.h contains a bunch of C stuff which won't build without __ASSEMBLY__ guards unless I'm missing something (I'm working with a non-mainline tree so I might be).
On Wed, May 28, 2014 at 09:19:55PM +0100, Mark Brown wrote:
On Wed, Apr 30, 2014 at 06:54:33PM +0900, AKASHI Takahiro wrote:
+/*
- arch/arm64/kernel/entry-ftrace.S
- Copyright (C) 2013 Linaro Limited
- Author: AKASHI Takahiro takahiro.akashi@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h>
asm/insn.h contains a bunch of C stuff which won't build without __ASSEMBLY__ guards unless I'm missing something (I'm working with a non-mainline tree so I might be).
The guards are added earlier in this series.
Will
On Thu, May 29, 2014 at 09:07:05AM +0100, Will Deacon wrote:
On Wed, May 28, 2014 at 09:19:55PM +0100, Mark Brown wrote:
On Wed, Apr 30, 2014 at 06:54:33PM +0900, AKASHI Takahiro wrote:
+#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h>
asm/insn.h contains a bunch of C stuff which won't build without __ASSEMBLY__ guards unless I'm missing something (I'm working with a non-mainline tree so I might be).
The guards are added earlier in this series.
Yeah, I discussed with Akashi-san later - the guard patch didn't actually make it onto the list properly, neither of us has a copy locally.
This patch allows "dynamic ftrace" if CONFIG_DYNAMIC_FTRACE is enabled. Here we can turn on and off tracing dynamically per-function base.
On arm64, this is done by patching single branch instruction to _mcount() inserted by gcc -pg option. The branch is replaced to NOP initially at kernel start up, and later on, NOP to branch to ftrace_caller() when enabled or branch to NOP when disabled. Please note that ftrace_caller() is a counterpart of _mcount() in case of 'static' ftrace.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Acked-by: Will Deacon will.deacon@arm.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 15 +++++ arch/arm64/kernel/entry-ftrace.S | 43 +++++++++++++++ arch/arm64/kernel/ftrace.c | 112 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 856007e..438a4ea 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -36,6 +36,7 @@ config ARM64 select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS + select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 58ea595..ed5c448 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,21 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); + +struct dyn_arch_ftrace { + /* No extra data needed for arm64 */ +}; + +extern unsigned long ftrace_graph_call; + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ + /* + * addr is the address of the mcount call instruction. + * recordmcount does the necessary offset calculation. + */ + return addr; +} #endif /* __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S index b2d8c45..b051871 100644 --- a/arch/arm64/kernel/entry-ftrace.S +++ b/arch/arm64/kernel/entry-ftrace.S @@ -86,6 +86,7 @@ add \reg, \reg, #8 .endm
+#ifndef CONFIG_DYNAMIC_FTRACE /* * void _mcount(unsigned long return_address) * @return_address: return address to instrumented function @@ -134,6 +135,48 @@ skip_ftrace_call: #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ENDPROC(_mcount)
+#else /* CONFIG_DYNAMIC_FTRACE */ +/* + * _mcount() is used to build the kernel with -pg option, but all the branch + * instructions to _mcount() are replaced to NOP initially at kernel start up, + * and later on, NOP to branch to ftrace_caller() when enabled or branch to + * NOP when disabled per-function base. + */ +ENTRY(_mcount) + ret +ENDPROC(_mcount) + +/* + * void ftrace_caller(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function is a counterpart of _mcount() in 'static' ftrace, and + * makes calls to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(ftrace_caller) + mcount_enter + + mcount_get_pc0 x0 // function's pc + mcount_get_lr x1 // function's lr + + .global ftrace_call +ftrace_call: // tracer(pc, lr); + nop // This will be replaced with "bl xxx" + // where xxx can be any kind of tracer. + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + .global ftrace_graph_call +ftrace_graph_call: // ftrace_graph_caller(); + nop // If enabled, this will be replaced + // "b ftrace_graph_caller" +#endif + + mcount_exit +ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */ + ENTRY(ftrace_stub) ret ENDPROC(ftrace_stub) diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index a559ab8..7924d73 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -17,6 +17,87 @@ #include <asm/ftrace.h> #include <asm/insn.h>
+#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Replace a single instruction, which may be a branch or NOP. + * If @validate == true, a replaced instruction is checked against 'old'. + */ +static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, + bool validate) +{ + u32 replaced; + + /* + * Note: + * Due to modules and __init, code can disappear and change, + * we need to protect against faulting as well as code changing. + * We do this by aarch64_insn_*() which use the probe_kernel_*(). + * + * No lock is held here because all the modifications are run + * through stop_machine(). + */ + if (validate) { + if (aarch64_insn_read((void *)pc, &replaced)) + return -EFAULT; + + if (replaced != old) + return -EINVAL; + } + if (aarch64_insn_patch_text_nosync((void *)pc, new)) + return -EPERM; + + return 0; +} + +/* + * Replace tracer function in ftrace_caller() + */ +int ftrace_update_ftrace_func(ftrace_func_t func) +{ + unsigned long pc; + u32 new; + + pc = (unsigned long)&ftrace_call; + new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); + + return ftrace_modify_code(pc, 0, new, false); +} + +/* + * Turn on the call to ftrace_caller() in instrumented function + */ +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + u32 old, new; + + old = aarch64_insn_gen_nop(); + new = aarch64_insn_gen_branch_imm(pc, addr, true); + + return ftrace_modify_code(pc, old, new, true); +} + +/* + * Turn off the call to ftrace_caller() in instrumented function + */ +int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, + unsigned long addr) +{ + unsigned long pc = rec->ip; + u32 old, new; + + old = aarch64_insn_gen_branch_imm(pc, addr, true); + new = aarch64_insn_gen_nop(); + + return ftrace_modify_code(pc, old, new, true); +} + +int __init ftrace_dyn_arch_init(void) +{ + return 0; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* * function_graph tracer expects ftrace_return_to_handler() to be called @@ -61,4 +142,35 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } } + +#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Turn on/off the call to ftrace_graph_caller() in ftrace_caller() + * depending on @enable. + */ +static int ftrace_modify_graph_caller(bool enable) +{ + unsigned long pc = (unsigned long)&ftrace_graph_call; + u32 branch, nop; + + branch = aarch64_insn_gen_branch_imm(pc, + (unsigned long)ftrace_graph_caller, false); + nop = aarch64_insn_gen_nop(); + + if (enable) + return ftrace_modify_code(pc, nop, branch, true); + else + return ftrace_modify_code(pc, branch, nop, true); +} + +int ftrace_enable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(true); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(false); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/ftrace.h | 5 +++- arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..41e8670 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,7 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); +extern void *return_address(unsigned int);
struct dyn_arch_ftrace { /* No extra data needed for arm64 */ @@ -33,6 +34,8 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) */ return addr; } -#endif /* __ASSEMBLY__ */ + +#define ftrace_return_address(n) return_address(n) +#endif /* ifndef __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 1ebcb4c..1f4d891 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -7,12 +7,13 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_insn.o = -pg +CFLAGS_REMOVE_return_address.o = -pg
# Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ - hyp-stub.o psci.o cpu_ops.o insn.o + hyp-stub.o psci.o cpu_ops.o insn.o return_address.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 0000000..89102a6 --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/* + * arch/arm64/kernel/return_address.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/export.h> +#include <linux/ftrace.h> + +#include <asm/stacktrace.h> + +struct return_address_data { + unsigned int level; + void *addr; +}; + +static int save_return_addr(struct stackframe *frame, void *d) +{ + struct return_address_data *data = d; + + if (!data->level) { + data->addr = (void *)frame->pc; + return 1; + } else { + --data->level; + return 0; + } +} + +void *return_address(unsigned int level) +{ + struct return_address_data data; + struct stackframe frame; + register unsigned long current_sp asm ("sp"); + + data.level = level + 2; + data.addr = NULL; + + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.pc = (unsigned long)return_address; /* dummy */ + + walk_stackframe(&frame, save_return_addr, &data); + + if (!data.level) + return data.addr; + else + return NULL; +} +EXPORT_SYMBOL_GPL(return_address);
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Please note that we can't trace compat system calls here because AArch32 mode does not share the same syscall table with AArch64. Just define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS in order to avoid unexpected results (bogus syscalls reported or even hang-up).
Acked-by: Will Deacon will.deacon@arm.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 18 ++++++++++++++++++ arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 9 +++++++++ 5 files changed, 31 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 438a4ea..0e9b8ce 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -48,6 +48,7 @@ config ARM64 select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP + select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 41e8670..c5534fa 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -17,6 +17,8 @@ #define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE
#ifndef __ASSEMBLY__ +#include <linux/compat.h> + extern void _mcount(unsigned long); extern void *return_address(unsigned int);
@@ -36,6 +38,22 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) }
#define ftrace_return_address(n) return_address(n) + +/* + * Because AArch32 mode does not share the same syscall table with AArch64, + * tracing compat syscalls may result in reporting bogus syscalls or even + * hang-up, so just do not trace them. + * See kernel/trace/trace_syscalls.c + * + * x86 code says: + * If the user realy wants these, then they should use the + * raw syscall tracepoints with filtering. + */ +#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS +static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) +{ + return is_compat_task(); +} #endif /* ifndef __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@
#include <linux/err.h>
+extern const void *sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index a4654c6..e5f47df 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -29,3 +29,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h> + +#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 4b58e81..0bf1955 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -42,6 +42,9 @@ #include <asm/traps.h> #include <asm/system_misc.h>
+#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> + /* * TODO: does not yet catch signals sent when the child dies. * in exit.c or in signal.c. @@ -1091,11 +1094,17 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) if (test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
+ if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_enter(regs, regs->syscallno); + return regs->syscallno; }
asmlinkage void syscall_trace_exit(struct pt_regs *regs) { + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_exit(regs, regs_return_value(regs)); + if (test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT); }
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Reviewed-by: Ganapatrao Kulkarni ganapatrao.kulkarni@cavium.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 2 + arch/arm64/include/asm/ftrace.h | 23 +++++ arch/arm64/kernel/Makefile | 4 + arch/arm64/kernel/arm64ksyms.c | 4 + arch/arm64/kernel/entry-ftrace.S | 175 +++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/ftrace.c | 64 ++++++++++++++ 6 files changed, 272 insertions(+) create mode 100644 arch/arm64/include/asm/ftrace.h create mode 100644 arch/arm64/kernel/entry-ftrace.S create mode 100644 arch/arm64/kernel/ftrace.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 340e344..6b3fef6 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -35,6 +35,8 @@ config ARM64 select HAVE_DMA_CONTIGUOUS select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FTRACE_MCOUNT_RECORD + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER select HAVE_GENERIC_DMA_COHERENT select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_MEMBLOCK diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 0000000..58ea595 --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,23 @@ +/* + * arch/arm64/include/asm/ftrace.h + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_FTRACE_H +#define __ASM_FTRACE_H + +#include <asm/insn.h> + +#define MCOUNT_ADDR ((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE AARCH64_INSN_SIZE + +#ifndef __ASSEMBLY__ +extern void _mcount(unsigned long); +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2d4554b..ac67fd0 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -5,6 +5,9 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg + # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ @@ -13,6 +16,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o +arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 338b568..7f0512f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -56,3 +56,7 @@ EXPORT_SYMBOL(clear_bit); EXPORT_SYMBOL(test_and_clear_bit); EXPORT_SYMBOL(change_bit); EXPORT_SYMBOL(test_and_change_bit); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 0000000..622846f --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,175 @@ +/* + * arch/arm64/kernel/entry-ftrace.S + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +/* + * Gcc with -pg will put the following code in the beginning of each function: + * mov x0, x30 + * bl _mcount + * [function's body ...] + * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic + * ftrace is enabled. + * + * Please note that x0 as an argument will not be used here because we can + * get lr(x30) of instrumented function at any time by winding up call stack + * as long as the kernel is compiled without -fomit-frame-pointer. + * (or CONFIG_FRAME_POINTER, this is forced on arm64) + * + * stack layout after mcount_enter in _mcount(): + * + * current sp/fp => 0:+-----+ + * in _mcount() | x29 | -> instrumented function's fp + * +-----+ + * | x30 | -> _mcount()'s lr (= instrumented function's pc) + * old sp => +16:+-----+ + * when instrumented | | + * function calls | ... | + * _mcount() | | + * | | + * instrumented => +xx:+-----+ + * function's fp | x29 | -> parent's fp + * +-----+ + * | x30 | -> instrumented function's lr (= parent's pc) + * +-----+ + * | ... | + */ + + .macro mcount_enter + stp x29, x30, [sp, #-16]! + mov x29, sp + .endm + + .macro mcount_exit + ldp x29, x30, [sp], #16 + ret + .endm + + .macro mcount_adjust_addr rd, rn + sub \rd, \rn, #AARCH64_INSN_SIZE + .endm + + /* for instrumented function's parent */ + .macro mcount_get_parent_fp reg + ldr \reg, [x29] + ldr \reg, [\reg] + .endm + + /* for instrumented function */ + .macro mcount_get_pc0 reg + mcount_adjust_addr \reg, x30 + .endm + + .macro mcount_get_pc reg + ldr \reg, [x29, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr reg + ldr \reg, [x29] + ldr \reg, [\reg, #8] + mcount_adjust_addr \reg, \reg + .endm + + .macro mcount_get_lr_addr reg + ldr \reg, [x29] + add \reg, \reg, #8 + .endm + +/* + * void _mcount(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function makes calls, if enabled, to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST + ldr x0, =ftrace_trace_stop + ldr x0, [x0] // if ftrace_trace_stop + ret // return; +#endif + mcount_enter + + ldr x0, =ftrace_trace_function + ldr x2, [x0] + adr x0, ftrace_stub + cmp x0, x2 // if (ftrace_trace_function + b.eq skip_ftrace_call // != ftrace_stub) { + + mcount_get_pc x0 // function's pc + mcount_get_lr x1 // function's lr (= parent's pc) + blr x2 // (*ftrace_trace_function)(pc, lr); + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call: // return; + mcount_exit // } +#else + mcount_exit // return; + // } +skip_ftrace_call: + ldr x1, =ftrace_graph_return + ldr x2, [x1] // if ((ftrace_graph_return + cmp x0, x2 // != ftrace_stub) + b.ne ftrace_graph_caller + + ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry + ldr x2, [x1] // != ftrace_graph_entry_stub)) + ldr x0, =ftrace_graph_entry_stub + cmp x0, x2 + b.ne ftrace_graph_caller // ftrace_graph_caller(); + + mcount_exit +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount) + +ENTRY(ftrace_stub) + ret +ENDPROC(ftrace_stub) + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * void ftrace_graph_caller(void) + * + * Called from _mcount() or ftrace_caller() when function_graph tracer is + * selected. + * This function w/ prepare_ftrace_return() fakes link register's value on + * the call stack in order to intercept instrumented function's return path + * and run return_to_handler() later on its exit. + */ +ENTRY(ftrace_graph_caller) + mcount_get_lr_addr x0 // pointer to function's saved lr + mcount_get_pc x1 // function's pc + mcount_get_parent_fp x2 // parent's fp + bl prepare_ftrace_return // prepare_ftrace_return(&lr, pc, fp) + + mcount_exit +ENDPROC(ftrace_graph_caller) + +/* + * void return_to_handler(void) + * + * Run ftrace_return_to_handler() before going back to parent. + * @fp is checked against the value passed by ftrace_graph_caller() + * only when CONFIG_FUNCTION_GRAPH_FP_TEST is enabled. + */ +ENTRY(return_to_handler) + str x0, [sp, #-16]! + mov x0, x29 // parent's fp + bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); + mov x30, x0 // restore the original return address + ldr x0, [sp], #16 + ret +END(return_to_handler) +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 0000000..a559ab8 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,64 @@ +/* + * arch/arm64/kernel/ftrace.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * function_graph tracer expects ftrace_return_to_handler() to be called + * on the way back to parent. For this purpose, this function is called + * in _mcount() or ftrace_caller() to replace return address (*parent) on + * the call stack to return_to_handler. + * + * Note that @frame_pointer is used only for sanity check later. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, + unsigned long frame_pointer) +{ + unsigned long return_hooker = (unsigned long)&return_to_handler; + unsigned long old; + struct ftrace_graph_ent trace; + int err; + + if (unlikely(atomic_read(¤t->tracing_graph_pause))) + return; + + /* + * Note: + * No protection against faulting at *parent, which may be seen + * on other archs. It's unlikely on AArch64. + */ + old = *parent; + *parent = return_hooker; + + trace.func = self_addr; + trace.depth = current->curr_ret_stack + 1; + + /* Only trace if the calling function expects to */ + if (!ftrace_graph_entry(&trace)) { + *parent = old; + return; + } + + err = ftrace_push_return_trace(old, self_addr, &trace.depth, + frame_pointer); + if (err == -EBUSY) { + *parent = old; + return; + } +} +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
On Sat, Mar 15, 2014 at 05:45:50AM +0000, AKASHI Takahiro wrote:
This patch implements arm64 specific part to support function tracers, such as function (CONFIG_FUNCTION_TRACER), function_graph (CONFIG_FUNCTION_GRAPH_TRACER) and function profiler (CONFIG_FUNCTION_PROFILER).
With 'function' tracer, all the functions in the kernel are traced with timestamps in ${sysfs}/tracing/trace. If function_graph tracer is specified, call graph is generated.
The kernel must be compiled with -pg option so that _mcount() is inserted at the beginning of functions. This function is called on every function's entry as long as tracing is enabled. In addition, function_graph tracer also needs to be able to probe function's exit. ftrace_graph_caller() & return_to_handler do this by faking link register's value to intercept function's return path.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Reviewed-by: Ganapatrao Kulkarni ganapatrao.kulkarni@cavium.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
I'm still slightly suspicious about the alignment of your ASCII art, but:
Acked-by: Will Deacon will.deacon@arm.com
Will
This patch allows "dynamic ftrace" if CONFIG_DYNAMIC_FTRACE is enabled. Here we can turn on and off tracing dynamically per-function base.
On arm64, this is done by patching single branch instruction to _mcount() inserted by gcc -pg option. The branch is replaced to NOP initially at kernel start up, and later on, NOP to branch to ftrace_caller() when enabled or branch to NOP when disabled. Please note that ftrace_caller() is a counterpart of _mcount() in case of 'static' ftrace.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 15 ++++++ arch/arm64/kernel/entry-ftrace.S | 43 +++++++++++++++ arch/arm64/kernel/ftrace.c | 114 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6b3fef6..6954959 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -33,6 +33,7 @@ config ARM64 select HAVE_DMA_API_DEBUG select HAVE_DMA_ATTRS select HAVE_DMA_CONTIGUOUS + select HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index 58ea595..ed5c448 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,21 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); + +struct dyn_arch_ftrace { + /* No extra data needed for arm64 */ +}; + +extern unsigned long ftrace_graph_call; + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ + /* + * addr is the address of the mcount call instruction. + * recordmcount does the necessary offset calculation. + */ + return addr; +} #endif /* __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S index 622846f..d0cad6d 100644 --- a/arch/arm64/kernel/entry-ftrace.S +++ b/arch/arm64/kernel/entry-ftrace.S @@ -86,6 +86,7 @@ add \reg, \reg, #8 .endm
+#ifndef CONFIG_DYNAMIC_FTRACE /* * void _mcount(unsigned long return_address) * @return_address: return address to instrumented function @@ -134,6 +135,48 @@ skip_ftrace_call: #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ ENDPROC(_mcount)
+#else /* CONFIG_DYNAMIC_FTRACE */ +/* + * _mcount() is used to build the kernel with -pg option, but all the branch + * instructions to _mcount() are replaced to NOP initially at kernel start up, + * and later on, NOP to branch to ftrace_caller() when enabled or branch to + * NOP when disabled per-function base. + */ +ENTRY(_mcount) + ret +ENDPROC(_mcount) + +/* + * void ftrace_caller(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function is a counterpart of _mcount() in 'static' ftrace, and + * makes calls to: + * - tracer function to probe instrumented function's entry, + * - ftrace_graph_caller to set up an exit hook + */ +ENTRY(ftrace_caller) + mcount_enter + + mcount_get_pc0 x0 // function's pc + mcount_get_lr x1 // function's lr + + .global ftrace_call +ftrace_call: // tracer(pc, lr); + nop // This will be replaced with "bl xxx" + // where xxx can be any kind of tracer. + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + .global ftrace_graph_call +ftrace_graph_call: // ftrace_graph_caller(); + nop // If enabled, this will be replaced + // "b ftrace_graph_caller" +#endif + + mcount_exit +ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */ + ENTRY(ftrace_stub) ret ENDPROC(ftrace_stub) diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index a559ab8..9f708e7 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c @@ -17,6 +17,89 @@ #include <asm/ftrace.h> #include <asm/insn.h>
+#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Replace a single instruction, which may be a branch or NOP. + * If @validate == true, a replaced instruction is checked against 'old'. + */ +static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, + bool validate) +{ + u32 replaced; + + /* + * Note: + * Due to modules and __init, code can disappear and change, + * we need to protect against faulting as well as code changing. + * We do this by aarch64_insn_*() which use the probe_kernel_*(). + * + * No lock is held here because all the modifications are run + * through stop_machine(). + */ + if (validate) { + if (aarch64_insn_read((void *)pc, &replaced)) + return -EFAULT; + + if (replaced != old) + return -EINVAL; + } + if (aarch64_insn_patch_text_nosync((void *)pc, new)) + return -EPERM; + + return 0; +} + +/* + * Replace tracer function in ftrace_caller() + */ +int ftrace_update_ftrace_func(ftrace_func_t func) +{ + unsigned long pc; + u32 new; + + pc = (unsigned long)&ftrace_call; + new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); + + return ftrace_modify_code(pc, 0, new, false); +} + +/* + * Turn on the call to ftrace_caller() in instrumented function + */ +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned long pc = rec->ip; + u32 old, new; + + old = aarch64_insn_gen_nop(); + new = aarch64_insn_gen_branch_imm(pc, addr, true); + + return ftrace_modify_code(pc, old, new, true); +} + +/* + * Turn off the call to ftrace_caller() in instrumented function + */ +int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, + unsigned long addr) +{ + unsigned long pc = rec->ip; + u32 old, new; + + old = aarch64_insn_gen_branch_imm(pc, addr, true); + new = aarch64_insn_gen_nop(); + + return ftrace_modify_code(pc, old, new, true); +} + +int __init ftrace_dyn_arch_init(void *data) +{ + *(unsigned long *)data = 0; + + return 0; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* * function_graph tracer expects ftrace_return_to_handler() to be called @@ -61,4 +144,35 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, return; } } + +#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Turn on/off the call to ftrace_graph_caller() in ftrace_caller() + * depending on @enable. + */ +static int ftrace_modify_graph_caller(bool enable) +{ + unsigned long pc = (unsigned long)&ftrace_graph_call; + u32 branch, nop; + + branch = aarch64_insn_gen_branch_imm(pc, + (unsigned long)ftrace_graph_caller, false); + nop = aarch64_insn_gen_nop(); + + if (enable) + return ftrace_modify_code(pc, nop, branch, true); + else + return ftrace_modify_code(pc, branch, nop, true); +} + +int ftrace_enable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(true); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ + return ftrace_modify_graph_caller(false); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
On Sat, Mar 15, 2014 at 05:45:51AM +0000, AKASHI Takahiro wrote:
This patch allows "dynamic ftrace" if CONFIG_DYNAMIC_FTRACE is enabled. Here we can turn on and off tracing dynamically per-function base.
On arm64, this is done by patching single branch instruction to _mcount() inserted by gcc -pg option. The branch is replaced to NOP initially at kernel start up, and later on, NOP to branch to ftrace_caller() when enabled or branch to NOP when disabled. Please note that ftrace_caller() is a counterpart of _mcount() in case of 'static' ftrace.
More details on architecture specific requirements are described in Documentation/trace/ftrace-design.txt.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Acked-by: Will Deacon will.deacon@arm.com
Will
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/include/asm/ftrace.h | 13 ++++++++- arch/arm64/kernel/Makefile | 3 ++- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..c44c4b1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,7 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); +extern void *return_address(unsigned int);
struct dyn_arch_ftrace { /* No extra data needed for arm64 */ @@ -33,6 +34,16 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) */ return addr; } -#endif /* __ASSEMBLY__ */ + +#define HAVE_ARCH_CALLER_ADDR + +#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#endif /* ifndef __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index ac67fd0..b5bfa7f 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -7,12 +7,13 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_insn.o = -pg +CFLAGS_REMOVE_return_address.o = -pg
# Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ - hyp-stub.o psci.o cpu_ops.o insn.o + hyp-stub.o psci.o cpu_ops.o insn.o return_address.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 0000000..89102a6 --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/* + * arch/arm64/kernel/return_address.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro takahiro.akashi@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/export.h> +#include <linux/ftrace.h> + +#include <asm/stacktrace.h> + +struct return_address_data { + unsigned int level; + void *addr; +}; + +static int save_return_addr(struct stackframe *frame, void *d) +{ + struct return_address_data *data = d; + + if (!data->level) { + data->addr = (void *)frame->pc; + return 1; + } else { + --data->level; + return 0; + } +} + +void *return_address(unsigned int level) +{ + struct return_address_data data; + struct stackframe frame; + register unsigned long current_sp asm ("sp"); + + data.level = level + 2; + data.addr = NULL; + + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.pc = (unsigned long)return_address; /* dummy */ + + walk_stackframe(&frame, save_return_addr, &data); + + if (!data.level) + return data.addr; + else + return NULL; +} +EXPORT_SYMBOL_GPL(return_address);
On Sat, Mar 15, 2014 at 05:45:52AM +0000, AKASHI Takahiro wrote:
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/include/asm/ftrace.h | 13 ++++++++- arch/arm64/kernel/Makefile | 3 ++- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..c44c4b1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,7 @@ #ifndef __ASSEMBLY__ extern void _mcount(unsigned long); +extern void *return_address(unsigned int); struct dyn_arch_ftrace { /* No extra data needed for arm64 */ @@ -33,6 +34,16 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) */ return addr; } -#endif /* __ASSEMBLY__ */
+#define HAVE_ARCH_CALLER_ADDR
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#endif /* ifndef __ASSEMBLY__ */
Hmm, I thought you were going to take a look at reworking the core code for this, as Steve and I suggested?
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-March/240239.html
Will
Hi Will,
On 04/16/2014 10:52 PM, Will Deacon wrote:
On Sat, Mar 15, 2014 at 05:45:52AM +0000, AKASHI Takahiro wrote:
CALLER_ADDRx returns caller's address at specified level in call stacks. They are used for several tracers like irqsoff and preemptoff. Strange to say, however, they are refered even without FTRACE.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
arch/arm64/include/asm/ftrace.h | 13 ++++++++- arch/arm64/kernel/Makefile | 3 ++- arch/arm64/kernel/return_address.c | 55 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/kernel/return_address.c
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index ed5c448..c44c4b1 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -18,6 +18,7 @@
#ifndef __ASSEMBLY__ extern void _mcount(unsigned long); +extern void *return_address(unsigned int);
struct dyn_arch_ftrace { /* No extra data needed for arm64 */ @@ -33,6 +34,16 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) */ return addr; } -#endif /* __ASSEMBLY__ */
+#define HAVE_ARCH_CALLER_ADDR
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) +#define CALLER_ADDR1 ((unsigned long)return_address(1)) +#define CALLER_ADDR2 ((unsigned long)return_address(2)) +#define CALLER_ADDR3 ((unsigned long)return_address(3)) +#define CALLER_ADDR4 ((unsigned long)return_address(4)) +#define CALLER_ADDR5 ((unsigned long)return_address(5)) +#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#endif /* ifndef __ASSEMBLY__ */
Hmm, I thought you were going to take a look at reworking the core code for this, as Steve and I suggested?
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-March/240239.html
Sorry that I've forgot to submit a new revision with this change. I will post it soon.
Thanks, -Takahiro AKASHI
Will
Will,
I made a separate branch called ftrace/arm64 as shown below. You can pull that branch to base the rest of Akashi-san's patches on top of it. The branch is based on top of v3.15-rc5.
-- Steve
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git ftrace/arm64
Head SHA1: eed542d6962ba33a689b4007a389f466e407bd74
AKASHI Takahiro (1): ftrace: Make CALLER_ADDRx macros more generic
---- arch/arm/include/asm/ftrace.h | 10 +--------- arch/blackfin/include/asm/ftrace.h | 11 +---------- arch/parisc/include/asm/ftrace.h | 10 +--------- arch/sh/include/asm/ftrace.h | 10 +--------- arch/xtensa/include/asm/ftrace.h | 14 ++++---------- include/linux/ftrace.h | 34 ++++++++++++++++++---------------- 6 files changed, 26 insertions(+), 63 deletions(-) --------------------------- commit eed542d6962ba33a689b4007a389f466e407bd74 Author: AKASHI Takahiro takahiro.akashi@linaro.org Date: Tue May 20 20:31:04 2014 +0900
ftrace: Make CALLER_ADDRx macros more generic
Most archs with HAVE_ARCH_CALLER_ADDR have pretty much the same definitions of CALLER_ADDRx(n). Instead of duplicating the code for all the archs, define a ftrace_return_address0() and ftrace_return_address(n) that can be overwritten by the archs if they need to do something different. Instead of 7 macros in every arch, we now only have at most 2 (and actually only 1 as ftrace_return_address0() should be the same for all archs).
The CALLER_ADDRx(n) will now be defined in linux/ftrace.h and use the ftrace_return_address*(n?) macros. This removes a lot of the duplicate code.
Link: http://lkml.kernel.org/p/1400585464-30333-1-git-send-email-takahiro.akashi@l...
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org Signed-off-by: Steven Rostedt rostedt@goodmis.org
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index f89515adac60..eb577f4f5f70 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -52,15 +52,7 @@ extern inline void *return_address(unsigned int level)
#endif
-#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_addr(n) return_address(n)
#endif /* ifndef __ASSEMBLY__ */
diff --git a/arch/blackfin/include/asm/ftrace.h b/arch/blackfin/include/asm/ftrace.h index 8a029505d7b7..2f1c3c2657ad 100644 --- a/arch/blackfin/include/asm/ftrace.h +++ b/arch/blackfin/include/asm/ftrace.h @@ -66,16 +66,7 @@ extern inline void *return_address(unsigned int level)
#endif /* CONFIG_FRAME_POINTER */
-#define HAVE_ARCH_CALLER_ADDR - -/* inline function or macro may lead to unexpected result */ -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h index 72c0fafaa039..544ed8ef87eb 100644 --- a/arch/parisc/include/asm/ftrace.h +++ b/arch/parisc/include/asm/ftrace.h @@ -24,15 +24,7 @@ extern void return_to_handler(void);
extern unsigned long return_address(unsigned int);
-#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#define CALLER_ADDR4 return_address(4) -#define CALLER_ADDR5 return_address(5) -#define CALLER_ADDR6 return_address(6) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/sh/include/asm/ftrace.h b/arch/sh/include/asm/ftrace.h index 13e9966464c2..e79fb6ebaa42 100644 --- a/arch/sh/include/asm/ftrace.h +++ b/arch/sh/include/asm/ftrace.h @@ -40,15 +40,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) /* arch/sh/kernel/return_address.c */ extern void *return_address(unsigned int);
-#define HAVE_ARCH_CALLER_ADDR - -#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -#define CALLER_ADDR1 ((unsigned long)return_address(1)) -#define CALLER_ADDR2 ((unsigned long)return_address(2)) -#define CALLER_ADDR3 ((unsigned long)return_address(3)) -#define CALLER_ADDR4 ((unsigned long)return_address(4)) -#define CALLER_ADDR5 ((unsigned long)return_address(5)) -#define CALLER_ADDR6 ((unsigned long)return_address(6)) +#define ftrace_return_address(n) return_address(n)
#endif /* __ASSEMBLY__ */
diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 736b9d214d80..6c6d9a9f185f 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -12,24 +12,18 @@
#include <asm/processor.h>
-#define HAVE_ARCH_CALLER_ADDR #ifndef __ASSEMBLY__ -#define CALLER_ADDR0 ({ unsigned long a0, a1; \ +#define ftrace_return_address0 ({ unsigned long a0, a1; \ __asm__ __volatile__ ( \ "mov %0, a0\n" \ "mov %1, a1\n" \ : "=r"(a0), "=r"(a1)); \ MAKE_PC_FROM_RA(a0, a1); }) + #ifdef CONFIG_FRAME_POINTER extern unsigned long return_address(unsigned level); -#define CALLER_ADDR1 return_address(1) -#define CALLER_ADDR2 return_address(2) -#define CALLER_ADDR3 return_address(3) -#else /* CONFIG_FRAME_POINTER */ -#define CALLER_ADDR1 (0) -#define CALLER_ADDR2 (0) -#define CALLER_ADDR3 (0) -#endif /* CONFIG_FRAME_POINTER */ +#define ftrace_return_address(n) return_address(n) +#endif #endif /* __ASSEMBLY__ */
#ifdef CONFIG_FUNCTION_TRACER diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index ae9504b4b67d..2018751cad9e 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -616,25 +616,27 @@ static inline void __ftrace_enabled_restore(int enabled) #endif }
-#ifndef HAVE_ARCH_CALLER_ADDR +/* All archs should have this, but we define it for consistency */ +#ifndef ftrace_return_address0 +# define ftrace_return_address0 __builtin_return_address(0) +#endif + +/* Archs may use other ways for ADDR1 and beyond */ +#ifndef ftrace_return_address # ifdef CONFIG_FRAME_POINTER -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1)) -# define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2)) -# define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3)) -# define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4)) -# define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5)) -# define CALLER_ADDR6 ((unsigned long)__builtin_return_address(6)) +# define ftrace_return_address(n) __builtin_return_address(n) # else -# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) -# define CALLER_ADDR1 0UL -# define CALLER_ADDR2 0UL -# define CALLER_ADDR3 0UL -# define CALLER_ADDR4 0UL -# define CALLER_ADDR5 0UL -# define CALLER_ADDR6 0UL +# define ftrace_return_address(n) 0UL # endif -#endif /* ifndef HAVE_ARCH_CALLER_ADDR */ +#endif + +#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0) +#define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1)) +#define CALLER_ADDR2 ((unsigned long)ftrace_return_address(2)) +#define CALLER_ADDR3 ((unsigned long)ftrace_return_address(3)) +#define CALLER_ADDR4 ((unsigned long)ftrace_return_address(4)) +#define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5)) +#define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6))
#ifdef CONFIG_IRQSOFF_TRACER extern void time_hardirqs_on(unsigned long a0, unsigned long a1);
On Tue, May 27, 2014 at 02:10:19PM +0100, Steven Rostedt wrote:
Will,
I made a separate branch called ftrace/arm64 as shown below. You can pull that branch to base the rest of Akashi-san's patches on top of it. The branch is based on top of v3.15-rc5.
Thanks Steve, I'll pull that in.
Akashi: please can you re-send your series, based on the branch below and including the acks/reviewed-by tags you got for v6 of the patches?
Cheers,
Will
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git ftrace/arm64
Head SHA1: eed542d6962ba33a689b4007a389f466e407bd74
Hi Will
On 05/28/2014 03:49 AM, Will Deacon wrote:
On Tue, May 27, 2014 at 02:10:19PM +0100, Steven Rostedt wrote:
Will,
I made a separate branch called ftrace/arm64 as shown below. You can pull that branch to base the rest of Akashi-san's patches on top of it. The branch is based on top of v3.15-rc5.
Thanks Steve, I'll pull that in.
Akashi: please can you re-send your series, based on the branch below and including the acks/reviewed-by tags you got for v6 of the patches?
Sorry again to have missed your e-mail :-) I'm sure that you can successfully apply all my patch series, without any modification, from: v6 arm64: prerequisites for audit and ftrace v8 arm64: Add ftrace support except for the patch, "[v8 1/8] ftrace: make CALLER_ADDRx macros more generic," which was replaced by a new version and already appears in Steven's repo.
Thank you, -Takahiro AKASHI
Cheers,
Will
git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace.git ftrace/arm64
Head SHA1: eed542d6962ba33a689b4007a389f466e407bd74
On Thu, May 29, 2014 at 06:27:31AM +0100, AKASHI Takahiro wrote:
Hi Will
Hi Akashi,
On 05/28/2014 03:49 AM, Will Deacon wrote:
On Tue, May 27, 2014 at 02:10:19PM +0100, Steven Rostedt wrote:
Will,
I made a separate branch called ftrace/arm64 as shown below. You can pull that branch to base the rest of Akashi-san's patches on top of it. The branch is based on top of v3.15-rc5.
Thanks Steve, I'll pull that in.
Akashi: please can you re-send your series, based on the branch below and including the acks/reviewed-by tags you got for v6 of the patches?
Sorry again to have missed your e-mail :-)
You should fix your filters!
I'm sure that you can successfully apply all my patch series, without any modification, from: v6 arm64: prerequisites for audit and ftrace v8 arm64: Add ftrace support except for the patch, "[v8 1/8] ftrace: make CALLER_ADDRx macros more generic," which was replaced by a new version and already appears in Steven's repo.
Yeah, you're right, I've managed to pick patches 2-8 from that posting -- thanks.
Will
This patch allows system call entry or exit to be traced as ftrace events, ie. sys_enter_*/sys_exit_*, if CONFIG_FTRACE_SYSCALLS is enabled. Those events appear and can be controlled under ${sysfs}/tracing/events/syscalls/
Please note that we can't trace compat system calls here because AArch32 mode does not share the same syscall table with AArch64. Just define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS in order to avoid unexpected results (bogus syscalls reported or even hang-up).
Acked-by: Will Deacon will.deacon@arm.com Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/ftrace.h | 18 ++++++++++++++++++ arch/arm64/include/asm/syscall.h | 1 + arch/arm64/include/asm/unistd.h | 2 ++ arch/arm64/kernel/ptrace.c | 9 +++++++++ 5 files changed, 31 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6954959..b1dcdb4 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -43,6 +43,7 @@ config ARM64 select HAVE_MEMBLOCK select HAVE_PATA_PLATFORM select HAVE_PERF_EVENTS + select HAVE_SYSCALL_TRACEPOINTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h index c44c4b1..7616255 100644 --- a/arch/arm64/include/asm/ftrace.h +++ b/arch/arm64/include/asm/ftrace.h @@ -44,6 +44,24 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) #define CALLER_ADDR4 ((unsigned long)return_address(4)) #define CALLER_ADDR5 ((unsigned long)return_address(5)) #define CALLER_ADDR6 ((unsigned long)return_address(6)) + +#include <linux/compat.h> + +/* + * Because AArch32 mode does not share the same syscall table with AArch64, + * tracing compat syscalls may result in reporting bogus syscalls or even + * hang-up, so just do not trace them. + * See kernel/trace/trace_syscalls.c + * + * x86 code says: + * If the user realy wants these, then they should use the + * raw syscall tracepoints with filtering. + */ +#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS +static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) +{ + return is_compat_task(); +} #endif /* ifndef __ASSEMBLY__ */
#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 70ba9d4..383771e 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@
#include <linux/err.h>
+extern const void *sys_call_table[];
static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs) diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217..c335479 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -28,3 +28,5 @@ #endif #define __ARCH_WANT_SYS_CLONE #include <uapi/asm/unistd.h> + +#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index c47a3ed..3ee76ed 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -42,6 +42,9 @@ #include <asm/traps.h> #include <asm/system_misc.h>
+#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> + /* * TODO: does not yet catch signals sent when the child dies. * in exit.c or in signal.c. @@ -1091,11 +1094,17 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) if (test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
+ if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_enter(regs, regs->syscallno); + return regs->syscallno; }
asmlinkage void syscall_trace_exit(struct pt_regs *regs) { + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + trace_sys_exit(regs, regs_return_value(regs)); + if (test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT); }
linaro-kernel@lists.linaro.org