diff options
| author | Rabin Vincent <rabin@rab.in> | 2010-08-10 14:43:28 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-09-02 10:27:40 -0400 |
| commit | 3b6c223b1b97ad60bbb0f4efda57d649414ac2a2 (patch) | |
| tree | 291dcb285e8cb64415a82ed1c65dc9681921a257 | |
| parent | f9810a82536e0c730c57844753e6c08cc7f77881 (diff) | |
ARM: 6318/1: ftrace: fix and update dynamic ftrace
This adds mcount recording and updates dynamic ftrace for ARM to work
with the new ftrace dyamic tracing implementation. It also adds support
for the mcount format used by newer ARM compilers.
With dynamic tracing, mcount() is implemented as a nop. Callsites are
patched on startup with nops, and dynamically patched to call to the
ftrace_caller() routine as needed.
Acked-by: Steven Rostedt <rostedt@goodmis.org> [recordmcount.pl change]
Signed-off-by: Rabin Vincent <rabin@rab.in>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
| -rw-r--r-- | arch/arm/include/asm/ftrace.h | 19 | ||||
| -rw-r--r-- | arch/arm/kernel/entry-common.S | 37 | ||||
| -rw-r--r-- | arch/arm/kernel/ftrace.c | 155 | ||||
| -rwxr-xr-x | scripts/recordmcount.pl | 2 |
4 files changed, 155 insertions, 58 deletions
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index 103f7ee97313..4a56a2ee067c 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h | |||
| @@ -2,12 +2,29 @@ | |||
| 2 | #define _ASM_ARM_FTRACE | 2 | #define _ASM_ARM_FTRACE |
| 3 | 3 | ||
| 4 | #ifdef CONFIG_FUNCTION_TRACER | 4 | #ifdef CONFIG_FUNCTION_TRACER |
| 5 | #define MCOUNT_ADDR ((long)(mcount)) | 5 | #define MCOUNT_ADDR ((unsigned long)(__gnu_mcount_nc)) |
| 6 | #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ | 6 | #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ |
| 7 | 7 | ||
| 8 | #ifndef __ASSEMBLY__ | 8 | #ifndef __ASSEMBLY__ |
| 9 | extern void mcount(void); | 9 | extern void mcount(void); |
| 10 | extern void __gnu_mcount_nc(void); | 10 | extern void __gnu_mcount_nc(void); |
| 11 | |||
| 12 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 13 | struct dyn_arch_ftrace { | ||
| 14 | #ifdef CONFIG_OLD_MCOUNT | ||
| 15 | bool old_mcount; | ||
| 16 | #endif | ||
| 17 | }; | ||
| 18 | |||
| 19 | static inline unsigned long ftrace_call_adjust(unsigned long addr) | ||
| 20 | { | ||
| 21 | return addr; | ||
| 22 | } | ||
| 23 | |||
| 24 | extern void ftrace_caller_old(void); | ||
| 25 | extern void ftrace_call_old(void); | ||
| 26 | #endif | ||
| 27 | |||
| 11 | #endif | 28 | #endif |
| 12 | 29 | ||
| 13 | #endif | 30 | #endif |
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index f5e75de0203e..e02790f28879 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S | |||
| @@ -127,6 +127,10 @@ ENDPROC(ret_from_fork) | |||
| 127 | * clobber the ip register. This is OK because the ARM calling convention | 127 | * clobber the ip register. This is OK because the ARM calling convention |
| 128 | * allows it to be clobbered in subroutines and doesn't use it to hold | 128 | * allows it to be clobbered in subroutines and doesn't use it to hold |
| 129 | * parameters.) | 129 | * parameters.) |
| 130 | * | ||
| 131 | * When using dynamic ftrace, we patch out the mcount call by a "mov r0, r0" | ||
| 132 | * for the mcount case, and a "pop {lr}" for the __gnu_mcount_nc case (see | ||
| 133 | * arch/arm/kernel/ftrace.c). | ||
| 130 | */ | 134 | */ |
| 131 | 135 | ||
| 132 | #ifndef CONFIG_OLD_MCOUNT | 136 | #ifndef CONFIG_OLD_MCOUNT |
| @@ -136,30 +140,45 @@ ENDPROC(ret_from_fork) | |||
| 136 | #endif | 140 | #endif |
| 137 | 141 | ||
| 138 | #ifdef CONFIG_DYNAMIC_FTRACE | 142 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 139 | ENTRY(mcount) | 143 | ENTRY(__gnu_mcount_nc) |
| 144 | mov ip, lr | ||
| 145 | ldmia sp!, {lr} | ||
| 146 | mov pc, ip | ||
| 147 | ENDPROC(__gnu_mcount_nc) | ||
| 148 | |||
| 149 | ENTRY(ftrace_caller) | ||
| 140 | stmdb sp!, {r0-r3, lr} | 150 | stmdb sp!, {r0-r3, lr} |
| 141 | mov r0, lr | 151 | mov r0, lr |
| 142 | sub r0, r0, #MCOUNT_INSN_SIZE | 152 | sub r0, r0, #MCOUNT_INSN_SIZE |
| 153 | ldr r1, [sp, #20] | ||
| 143 | 154 | ||
| 144 | .globl mcount_call | 155 | .global ftrace_call |
| 145 | mcount_call: | 156 | ftrace_call: |
| 146 | bl ftrace_stub | 157 | bl ftrace_stub |
| 147 | ldr lr, [fp, #-4] @ restore lr | 158 | ldmia sp!, {r0-r3, ip, lr} |
| 148 | ldmia sp!, {r0-r3, pc} | 159 | mov pc, ip |
| 160 | ENDPROC(ftrace_caller) | ||
| 161 | |||
| 162 | #ifdef CONFIG_OLD_MCOUNT | ||
| 163 | ENTRY(mcount) | ||
| 164 | stmdb sp!, {lr} | ||
| 165 | ldr lr, [fp, #-4] | ||
| 166 | ldmia sp!, {pc} | ||
| 149 | ENDPROC(mcount) | 167 | ENDPROC(mcount) |
| 150 | 168 | ||
| 151 | ENTRY(ftrace_caller) | 169 | ENTRY(ftrace_caller_old) |
| 152 | stmdb sp!, {r0-r3, lr} | 170 | stmdb sp!, {r0-r3, lr} |
| 153 | ldr r1, [fp, #-4] | 171 | ldr r1, [fp, #-4] |
| 154 | mov r0, lr | 172 | mov r0, lr |
| 155 | sub r0, r0, #MCOUNT_INSN_SIZE | 173 | sub r0, r0, #MCOUNT_INSN_SIZE |
| 156 | 174 | ||
| 157 | .globl ftrace_call | 175 | .globl ftrace_call_old |
| 158 | ftrace_call: | 176 | ftrace_call_old: |
| 159 | bl ftrace_stub | 177 | bl ftrace_stub |
| 160 | ldr lr, [fp, #-4] @ restore lr | 178 | ldr lr, [fp, #-4] @ restore lr |
| 161 | ldmia sp!, {r0-r3, pc} | 179 | ldmia sp!, {r0-r3, pc} |
| 162 | ENDPROC(ftrace_caller) | 180 | ENDPROC(ftrace_caller_old) |
| 181 | #endif | ||
| 163 | 182 | ||
| 164 | #else | 183 | #else |
| 165 | 184 | ||
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 0298286ad4ad..f09014cfbf2c 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c | |||
| @@ -2,102 +2,161 @@ | |||
| 2 | * Dynamic function tracing support. | 2 | * Dynamic function tracing support. |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com> | 4 | * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com> |
| 5 | * Copyright (C) 2010 Rabin Vincent <rabin@rab.in> | ||
| 5 | * | 6 | * |
| 6 | * For licencing details, see COPYING. | 7 | * For licencing details, see COPYING. |
| 7 | * | 8 | * |
| 8 | * Defines low-level handling of mcount calls when the kernel | 9 | * Defines low-level handling of mcount calls when the kernel |
| 9 | * is compiled with the -pg flag. When using dynamic ftrace, the | 10 | * is compiled with the -pg flag. When using dynamic ftrace, the |
| 10 | * mcount call-sites get patched lazily with NOP till they are | 11 | * mcount call-sites get patched with NOP till they are enabled. |
| 11 | * enabled. All code mutation routines here take effect atomically. | 12 | * All code mutation routines here are called under stop_machine(). |
| 12 | */ | 13 | */ |
| 13 | 14 | ||
| 14 | #include <linux/ftrace.h> | 15 | #include <linux/ftrace.h> |
| 16 | #include <linux/uaccess.h> | ||
| 15 | 17 | ||
| 16 | #include <asm/cacheflush.h> | 18 | #include <asm/cacheflush.h> |
| 17 | #include <asm/ftrace.h> | 19 | #include <asm/ftrace.h> |
| 18 | 20 | ||
| 19 | #define PC_OFFSET 8 | 21 | #define NOP 0xe8bd4000 /* pop {lr} */ |
| 20 | #define BL_OPCODE 0xeb000000 | ||
| 21 | #define BL_OFFSET_MASK 0x00ffffff | ||
| 22 | 22 | ||
| 23 | static unsigned long bl_insn; | 23 | #ifdef CONFIG_OLD_MCOUNT |
| 24 | static const unsigned long NOP = 0xe1a00000; /* mov r0, r0 */ | 24 | #define OLD_MCOUNT_ADDR ((unsigned long) mcount) |
| 25 | #define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old) | ||
| 25 | 26 | ||
| 26 | unsigned char *ftrace_nop_replace(void) | 27 | #define OLD_NOP 0xe1a00000 /* mov r0, r0 */ |
| 28 | |||
| 29 | static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec) | ||
| 30 | { | ||
| 31 | return rec->arch.old_mcount ? OLD_NOP : NOP; | ||
| 32 | } | ||
| 33 | |||
| 34 | static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) | ||
| 35 | { | ||
| 36 | if (!rec->arch.old_mcount) | ||
| 37 | return addr; | ||
| 38 | |||
| 39 | if (addr == MCOUNT_ADDR) | ||
| 40 | addr = OLD_MCOUNT_ADDR; | ||
| 41 | else if (addr == FTRACE_ADDR) | ||
| 42 | addr = OLD_FTRACE_ADDR; | ||
| 43 | |||
| 44 | return addr; | ||
| 45 | } | ||
| 46 | #else | ||
| 47 | static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec) | ||
| 48 | { | ||
| 49 | return NOP; | ||
| 50 | } | ||
| 51 | |||
| 52 | static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) | ||
| 27 | { | 53 | { |
| 28 | return (char *)&NOP; | 54 | return addr; |
| 29 | } | 55 | } |
| 56 | #endif | ||
| 30 | 57 | ||
| 31 | /* construct a branch (BL) instruction to addr */ | 58 | /* construct a branch (BL) instruction to addr */ |
| 32 | unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr) | ||
