This series contains fixes and improvements to the ARM ftrace support. It adds
Thumb-2 support and re-implements the dynamic ftrace support.
"ftrace: allow building without frame pointers" and "ftrace: pass KBUILD_CFLAGS
to record_mcount.pl" are non-ARM-specific ftrace patches which are required to
support the ARM functionality.
Cleanups / docs:
ARM: ftrace: clean up mcount assembly indentation
ARM: ftrace: document mcount formats
Thumb-2:
ftrace: allow building without frame pointers
ARM: ftrace: allow building without frame pointers
ARM: ftrace: add ENDPROC annotations
ARM: ftrace: add Thumb-2 support
Dynamic ftrace:
ftrace: pass KBUILD_CFLAGS to record_mcount.pl
ARM: ftrace: fix and update dynamic ftrace
ARM: ftrace: add Thumb-2 support to dynamic ftrace
ARM: ftrace: enable dynamic ftrace
Makefile | 7 ++
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 5 +
arch/arm/include/asm/ftrace.h | 20 ++++-
arch/arm/kernel/armksyms.c | 2 +
arch/arm/kernel/entry-common.S | 178 +++++++++++++++++++++++++++-----------
arch/arm/kernel/ftrace.c | 188 +++++++++++++++++++++++++++++----------
kernel/trace/Kconfig | 2 +-
scripts/Makefile.build | 3 +-
scripts/recordmcount.pl | 2 +
10 files changed, 307 insertions(+), 102 deletions(-)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
- Without the BSYM, the following assembler errors appear:
entry-common.S: Assembler messages:
entry-common.S:179: Error: invalid immediate for address calculation (value = 0x00000004)
- Without the orr, the lsb is not set on the pointer loaded from
ftrace_trace_function, but is set on BSYM(ftrace_stub), leading to the
comparison failing even when the pointer is pointing to ftrace_stub.
- The problem with the "mov lr, pc", is that it does not set the lsb when
storing the pc in lr. The called function returns with "bx lr", and the
mode changes to ARM. The blx is to avoid this.
Cc: Catalin Marinas <catalin...@arm.com>
Signed-off-by: Rabin Vincent <ra...@rab.in>
---
arch/arm/kernel/entry-common.S | 8 +++++---
1 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index c3bdb05..573ed3b 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -167,7 +167,8 @@ ENTRY(__gnu_mcount_nc)
stmdb sp!, {r0-r3, lr}
ldr r0, =ftrace_trace_function
ldr r2, [r0]
- adr r0, ftrace_stub
+ THUMB( orr r2, r2, #1 )
+ adr r0, BSYM(ftrace_stub)
cmp r0, r2
bne gnu_trace
ldmia sp!, {r0-r3, ip, lr}
@@ -177,8 +178,9 @@ gnu_trace:
ldr r1, [sp, #20] @ lr of instrumented routine
mov r0, lr
sub r0, r0, #MCOUNT_INSN_SIZE
- mov lr, pc
- mov pc, r2
+ ARM( mov lr, pc )
+ ARM( mov pc, r2 )
+ THUMB( blx r2 )
ldmia sp!, {r0-r3, ip, lr}
mov pc, ip
ENDPROC(__gnu_mcount_nc)
--
1.7.0
On ARM, it is not possible to build a Thumb-2 kernel with
-fno-omit-frame-pointer (FRAME_POINTERS depends on !THUMB2_KERNEL). In
order to support ftrace for Thumb-2, we need to be able to allow a
combination of FUNCTION_TRACER and !FRAME_POINTER. We do this by
omitting -fomit-frame-pointer if ftrace is enabled.
Acked-by: Frederic Weisbecker <fwei...@gmail.com>
Signed-off-by: Rabin Vincent <ra...@rab.in>
---
v2: Better comment.
Makefile | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
index 08ff02d..61404e0 100644
--- a/Makefile
+++ b/Makefile
@@ -546,8 +546,15 @@ endif
ifdef CONFIG_FRAME_POINTER
KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
else
+# Some targets (ARM with Thumb2, for example), can't be built with frame
+# pointers. For those, we don't have FUNCTION_TRACER automatically
+# select FRAME_POINTER. However, FUNCTION_TRACER adds -pg, and this is
+# incompatible with -fomit-frame-pointer with current GCC, so we don't use
+# -fomit-frame-pointer with FUNCTION_TRACER.
+ifndef CONFIG_FUNCTION_TRACER
KBUILD_CFLAGS += -fomit-frame-pointer
endif
+endif
ifdef CONFIG_DEBUG_INFO
KBUILD_CFLAGS += -g
--
1.7.0
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.
Signed-off-by: Rabin Vincent <ra...@rab.in>
---
arch/arm/include/asm/ftrace.h | 19 +++++-
arch/arm/kernel/entry-common.S | 37 +++++++---
arch/arm/kernel/ftrace.c | 155 +++++++++++++++++++++++++++------------
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 103f7ee..4a56a2e 100644
--- a/arch/arm/include/asm/ftrace.h
+++ b/arch/arm/include/asm/ftrace.h
@@ -2,12 +2,29 @@
#define _ASM_ARM_FTRACE
#ifdef CONFIG_FUNCTION_TRACER
-#define MCOUNT_ADDR ((long)(mcount))
+#define MCOUNT_ADDR ((unsigned long)(__gnu_mcount_nc))
#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
#ifndef __ASSEMBLY__
extern void mcount(void);
extern void __gnu_mcount_nc(void);
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+struct dyn_arch_ftrace {
+#ifdef CONFIG_OLD_MCOUNT
+ bool old_mcount;
+#endif
+};
+
+static inline unsigned long ftrace_call_adjust(unsigned long addr)
+{
+ return addr;
+}
+
+extern void ftrace_caller_old(void);
+extern void ftrace_call_old(void);
+#endif
+
#endif
#endif
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 573ed3b..9931a02 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -127,6 +127,10 @@ ENDPROC(ret_from_fork)
* clobber the ip register. This is OK because the ARM calling convention
* allows it to be clobbered in subroutines and doesn't use it to hold
* parameters.)
+ *
+ * When using dynamic ftrace, we patch out the mcount call by a "mov r0, r0"
+ * for the mcount case, and a "pop {lr}" for the __gnu_mcount_nc case (see
+ * arch/arm/kernel/ftrace.c).
*/
#ifndef CONFIG_OLD_MCOUNT
@@ -136,30 +140,45 @@ ENDPROC(ret_from_fork)
#endif
#ifdef CONFIG_DYNAMIC_FTRACE
-ENTRY(mcount)
+ENTRY(__gnu_mcount_nc)
+ mov ip, lr
+ ldmia sp!, {lr}
+ mov pc, ip
+ENDPROC(__gnu_mcount_nc)
+
+ENTRY(ftrace_caller)
stmdb sp!, {r0-r3, lr}
mov r0, lr
sub r0, r0, #MCOUNT_INSN_SIZE
+ ldr r1, [sp, #20]
- .globl mcount_call
-mcount_call:
+ .global ftrace_call
+ftrace_call:
bl ftrace_stub
- ldr lr, [fp, #-4] @ restore lr
- ldmia sp!, {r0-r3, pc}
+ ldmia sp!, {r0-r3, ip, lr}
+ mov pc, ip
+ENDPROC(ftrace_caller)
+
+#ifdef CONFIG_OLD_MCOUNT
+ENTRY(mcount)
+ stmdb sp!, {lr}
+ ldr lr, [fp, #-4]
+ ldmia sp!, {pc}
ENDPROC(mcount)
-ENTRY(ftrace_caller)
+ENTRY(ftrace_caller_old)
stmdb sp!, {r0-r3, lr}
ldr r1, [fp, #-4]
mov r0, lr
sub r0, r0, #MCOUNT_INSN_SIZE
- .globl ftrace_call
-ftrace_call:
+ .globl ftrace_call_old
+ftrace_call_old:
bl ftrace_stub
ldr lr, [fp, #-4] @ restore lr
ldmia sp!, {r0-r3, pc}
-ENDPROC(ftrace_caller)
+ENDPROC(ftrace_caller_old)
+#endif
#else
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index c638427..f09014c 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -2,102 +2,161 @@
* Dynamic function tracing support.
*
* Copyright (C) 2008 Abhishek Sagar <sagar.a...@gmail.com>
+ * Copyright (C) 2010 Rabin Vincent <ra...@rab.in>
*
* For licencing details, see COPYING.
*
* Defines low-level handling of mcount calls when the kernel
* is compiled with the -pg flag. When using dynamic ftrace, the
- * mcount call-sites get patched lazily with NOP till they are
- * enabled. All code mutation routines here take effect atomically.
+ * mcount call-sites get patched with NOP till they are enabled.
+ * All code mutation routines here are called under stop_machine().
*/
#include <linux/ftrace.h>
+#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#include <asm/ftrace.h>
-#define PC_OFFSET 8
-#define BL_OPCODE 0xeb000000
-#define BL_OFFSET_MASK 0x00ffffff
+#define NOP 0xe8bd4000 /* pop {lr} */
-static unsigned long bl_insn;
-static const unsigned long NOP = 0xe1a00000; /* mov r0, r0 */
+#ifdef CONFIG_OLD_MCOUNT
+#define OLD_MCOUNT_ADDR ((unsigned long) mcount)
+#define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old)
-unsigned char *ftrace_nop_replace(void)
+#define OLD_NOP 0xe1a00000 /* mov r0, r0 */
+
+static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
+{
+ return rec->arch.old_mcount ? OLD_NOP : NOP;
+}
+
+static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr)
+{
+ if (!rec->arch.old_mcount)
+ return addr;
+
+ if (addr == MCOUNT_ADDR)
+ addr = OLD_MCOUNT_ADDR;
+ else if (addr == FTRACE_ADDR)
+ addr = OLD_FTRACE_ADDR;
+
+ return addr;
+}
+#else
+static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
+{
+ return NOP;
+}
+
+static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr)
{
- return (char *)&NOP;
+ return addr;
}
+#endif
/* construct a branch (BL) instruction to addr */
-unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr)
+static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
{
long offset;
- offset = (long)addr - (long)(pc + PC_OFFSET);
+ offset = (long)addr - (long)(pc + 8);
if (unlikely(offset < -33554432 || offset > 33554428)) {
/* Can't generate branches that far (from ARM ARM). Ftrace
* doesn't generate branches outside of kernel text.
*/
WARN_ON_ONCE(1);
- return NULL;
+ return 0;
}
- offset = (offset >> 2) & BL_OFFSET_MASK;
- bl_insn = BL_OPCODE | offset;
- return (unsigned char *)&bl_insn;
-}
-int ftrace_modify_code(unsigned long pc, unsigned char *old_code,
- unsigned char *new_code)
-{
- unsigned long err = 0, replaced = 0, old, new;
+ offset = (offset >> 2) & 0x00ffffff;
- old = *(unsigned long *)old_code;
- new = *(unsigned long *)new_code;
+ return 0xeb000000 | offset;
+}
- __asm__ __volatile__ (
- "1: ldr %1, [%2] \n"
- " cmp %1, %4 \n"
- "2: streq %3, [%2] \n"
- " cmpne %1, %3 \n"
- " movne %0, #2 \n"
- "3:\n"
+static int ftrace_modify_code(unsigned long pc, unsigned long old,
+ unsigned long new)
+{
+ unsigned long replaced;
- ".section .fixup, \"ax\"\n"
- "4: mov %0, #1 \n"
- " b 3b \n"
- ".previous\n"
+ if (probe_kernel_read(&replaced, (void *)pc, MCOUNT_INSN_SIZE))
+ return -EFAULT;
- ".section __ex_table, \"a\"\n"
- " .long 1b, 4b \n"
- " .long 2b, 4b \n"
- ".previous\n"
+ if (replaced != old)
+ return -EINVAL;
- : "=r"(err), "=r"(replaced)
- : "r"(pc), "r"(new), "r"(old), "0"(err), "1"(replaced)
- : "memory");
+ if (probe_kernel_write((void *)pc, &new, MCOUNT_INSN_SIZE))
+ return -EPERM;
- if (!err && (replaced == old))
- flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
+ flush_icache_range(pc, pc + MCOUNT_INSN_SIZE);
- return err;
+ return 0;
}
int ftrace_update_ftrace_func(ftrace_func_t func)
{
- int ret;
unsigned long pc, old;
- unsigned char *new;
+ unsigned long new;
+ int ret;
pc = (unsigned long)&ftrace_call;
memcpy(&old, &ftrace_call, MCOUNT_INSN_SIZE);
new = ftrace_call_replace(pc, (unsigned long)func);
- ret = ftrace_modify_code(pc, (unsigned char *)&old, new);
+
+ ret = ftrace_modify_code(pc, old, new);
+
+#ifdef CONFIG_OLD_MCOUNT
+ if (!ret) {
+ pc = (unsigned long)&ftrace_call_old;
+ memcpy(&old, &ftrace_call_old, MCOUNT_INSN_SIZE);
+ new = ftrace_call_replace(pc, (unsigned long)func);
+
+ ret = ftrace_modify_code(pc, old, new);
+ }
+#endif
+
+ return ret;
+}
+
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+ unsigned long new, old;
+ unsigned long ip = rec->ip;
+
+ old = ftrace_nop_replace(rec);
+ new = ftrace_call_replace(ip, adjust_address(rec, addr));
+
+ return ftrace_modify_code(rec->ip, old, new);
+}
+
+int ftrace_make_nop(struct module *mod,
+ struct dyn_ftrace *rec, unsigned long addr)
+{
+ unsigned long ip = rec->ip;
+ unsigned long old;
+ unsigned long new;
+ int ret;
+
+ old = ftrace_call_replace(ip, adjust_address(rec, addr));
+ new = ftrace_nop_replace(rec);
+ ret = ftrace_modify_code(ip, old, new);
+
+#ifdef CONFIG_OLD_MCOUNT
+ if (ret == -EINVAL && addr == MCOUNT_ADDR) {
+ rec->arch.old_mcount = true;
+
+ old = ftrace_call_replace(ip, adjust_address(rec, addr));
+ new = ftrace_nop_replace(rec);
+ ret = ftrace_modify_code(ip, old, new);
+ }
+#endif
+
return ret;
}
-/* run from ftrace_init with irqs disabled */
int __init ftrace_dyn_arch_init(void *data)
{
- ftrace_mcount_set(data);
+ *(unsigned long *)data = 0;
+
return 0;
}
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index f3c9c0a..319af1e 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -268,6 +268,8 @@ if ($arch eq "x86_64") {
} elsif ($arch eq "arm") {
$alignment = 2;
$section_type = '%progbits';
+ $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_ARM_(CALL|PC24)" .
+ "\\s+(__gnu_mcount_nc|mcount)\$";
} elsif ($arch eq "ia64") {
$mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$";
--
1.7.0
Acked-by: Frederic Weisbecker <fwei...@gmail.com>
Signed-off-by: Rabin Vincent <ra...@rab.in>
---
v2: Reword/expand comment.
arch/arm/kernel/entry-common.S | 36 ++++++++++++++++++++++++++++++++++++
1 files changed, 36 insertions(+), 0 deletions(-)
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 0b042bd..f05a35a 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -92,6 +92,42 @@ ENDPROC(ret_from_fork)
#define CALL(x) .long x
#ifdef CONFIG_FUNCTION_TRACER
+/*
+ * When compiling with -pg, gcc inserts a call to the mcount routine at the
+ * start of every function. In mcount, apart from the function's address (in
+ * lr), we need to get hold of the function's caller's address.
+ *
+ * Older GCCs (pre-4.4) inserted a call to a routine called mcount like this:
+ *
+ * bl mcount
+ *
+ * These versions have the limitation that in order for the mcount routine to
+ * be able to determine the function's caller's address, an APCS-style frame
+ * pointer (which is set up with something like the code below) is required.
+ *
+ * mov ip, sp
+ * push {fp, ip, lr, pc}
+ * sub fp, ip, #4
+ *
+ * With EABI, these frame pointers are not available unless -mapcs-frame is
+ * specified, and if building as Thumb-2, not even then.
+ *
+ * Newer GCCs (4.4+) solve this problem by introducing a new version of mcount,
+ * with call sites like:
+ *
+ * push {lr}
+ * bl __gnu_mcount_nc
+ *
+ * With these compilers, frame pointers are not necessary.
+ *
+ * mcount can be thought of as a function called in the middle of a subroutine
+ * call. As such, it needs to be transparent for both the caller and the
+ * callee: the original lr needs to be restored when leaving mcount, and no
+ * registers should be clobbered. (In the __gnu_mcount_nc implementation, we
+ * clobber the ip register. This is OK because the ARM calling convention
+ * allows it to be clobbered in subroutines and doesn't use it to hold
+ * parameters.)
+ */
#ifdef CONFIG_DYNAMIC_FTRACE
ENTRY(mcount)
stmdb sp!, {r0-r3, lr}
--
1.7.0
Signed-off-by: Rabin Vincent <ra...@rab.in>
---
scripts/Makefile.build | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 0b94d2f..2535c11 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -209,7 +209,8 @@ ifdef CONFIG_FTRACE_MCOUNT_RECORD
cmd_record_mcount = set -e ; perl $(srctree)/scripts/recordmcount.pl "$(ARCH)" \
"$(if $(CONFIG_CPU_BIG_ENDIAN),big,little)" \
"$(if $(CONFIG_64BIT),64,32)" \
- "$(OBJDUMP)" "$(OBJCOPY)" "$(CC)" "$(LD)" "$(NM)" "$(RM)" "$(MV)" \
+ "$(OBJDUMP)" "$(OBJCOPY)" "$(CC) $(KBUILD_CFLAGS)" \
+ "$(LD)" "$(NM)" "$(RM)" "$(MV)" \
"$(if $(part-of-module),1,0)" "$(@)";
endif
--
1.7.0
Signed-off-by: Rabin Vincent <ra...@rab.in>
---
arch/arm/Kconfig | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index cadfe2e..a264837 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -18,6 +18,8 @@ config ARM
select HAVE_KPROBES if (!XIP_KERNEL)
select HAVE_KRETPROBES if (HAVE_KPROBES)
select HAVE_FUNCTION_TRACER if (!XIP_KERNEL)
+ select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL)
+ select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL)
select HAVE_GENERIC_DMA_COHERENT
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZO
--
1.7.0
Signed-off-by: Rabin Vincent <ra...@rab.in>
---
v2: #error if incompatible GCC.
arch/arm/Kconfig.debug | 5 +++++
arch/arm/kernel/armksyms.c | 2 ++
arch/arm/kernel/entry-common.S | 14 ++++++++++++++
kernel/trace/Kconfig | 2 +-
4 files changed, 22 insertions(+), 1 deletions(-)
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 91344af..4dbce53 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -27,6 +27,11 @@ config ARM_UNWIND
the performance is not affected. Currently, this feature
only works with EABI compilers. If unsure say Y.
+config OLD_MCOUNT
+ bool
+ depends on FUNCTION_TRACER && FRAME_POINTER
+ default y
+
config DEBUG_USER
bool "Verbose user fault messages"
help
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 8214bfe..e5e1e53 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -165,6 +165,8 @@ EXPORT_SYMBOL(_find_next_bit_be);
#endif
#ifdef CONFIG_FUNCTION_TRACER
+#ifdef CONFIG_OLD_MCOUNT
EXPORT_SYMBOL(mcount);
+#endif
EXPORT_SYMBOL(__gnu_mcount_nc);
#endif
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index f05a35a..6805a72 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -128,6 +128,13 @@ ENDPROC(ret_from_fork)
* allows it to be clobbered in subroutines and doesn't use it to hold
* parameters.)
*/
+
+#ifndef CONFIG_OLD_MCOUNT
+#if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))
+#error Ftrace requires CONFIG_FRAME_POINTER=y with GCC older than 4.4.0.
+#endif
+#endif
+
#ifdef CONFIG_DYNAMIC_FTRACE
ENTRY(mcount)
stmdb sp!, {r0-r3, lr}
@@ -173,6 +180,12 @@ gnu_trace:
ldmia sp!, {r0-r3, ip, lr}
mov pc, ip
+#ifdef CONFIG_OLD_MCOUNT
+/*
+ * This is under an ifdef in order to force link-time errors for people trying
+ * to build with !FRAME_POINTER with a GCC which doesn't use the new-style
+ * mcount.
+ */
ENTRY(mcount)
stmdb sp!, {r0-r3, lr}
ldr r0, =ftrace_trace_function
@@ -191,6 +204,7 @@ trace:
mov pc, r2
ldr lr, [fp, #-4] @ restore lr
ldmia sp!, {r0-r3, pc}
+#endif
#endif /* CONFIG_DYNAMIC_FTRACE */
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 13e13d4..e10519f 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -124,7 +124,7 @@ if FTRACE
config FUNCTION_TRACER
bool "Kernel Function Tracer"
depends on HAVE_FUNCTION_TRACER
- select FRAME_POINTER
+ select FRAME_POINTER if (!ARM_UNWIND)
select KALLSYMS
select GENERIC_TRACER
select CONTEXT_SWITCH_TRACER
--
1.7.0
Cc: Catalin Marinas <catalin...@arm.com>
Acked-by: Steven Rostedt <ros...@goodmis.org> [recordmcount.pl change]
Signed-off-by: Rabin Vincent <ra...@rab.in>
---
v2: #ifdef in ftrace_call_adjust replaced with comment.
arch/arm/include/asm/ftrace.h | 3 ++-
arch/arm/kernel/ftrace.c | 33 +++++++++++++++++++++++++++++++++
scripts/recordmcount.pl | 2 +-
3 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h
index 4a56a2e..f89515a 100644
--- a/arch/arm/include/asm/ftrace.h
+++ b/arch/arm/include/asm/ftrace.h
@@ -18,7 +18,8 @@ struct dyn_arch_ftrace {
static inline unsigned long ftrace_call_adjust(unsigned long addr)
{
- return addr;
+ /* With Thumb-2, the recorded addresses have the lsb set */
+ return addr & ~1;
}
extern void ftrace_caller_old(void);
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index f09014c..971ac8c 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -18,7 +18,11 @@
#include <asm/cacheflush.h>
#include <asm/ftrace.h>
+#ifdef CONFIG_THUMB2_KERNEL
+#define NOP 0xeb04f85d /* pop.w {lr} */
+#else
#define NOP 0xe8bd4000 /* pop {lr} */
+#endif
#ifdef CONFIG_OLD_MCOUNT
#define OLD_MCOUNT_ADDR ((unsigned long) mcount)
@@ -56,6 +60,34 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr)
#endif
/* construct a branch (BL) instruction to addr */
+#ifdef CONFIG_THUMB2_KERNEL
+static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
+{
+ unsigned long s, j1, j2, i1, i2, imm10, imm11;
+ unsigned long first, second;
+ long offset;
+
+ offset = (long)addr - (long)(pc + 4);
+ if (offset < -16777216 || offset > 16777214) {
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+ s = (offset >> 24) & 0x1;
+ i1 = (offset >> 23) & 0x1;
+ i2 = (offset >> 22) & 0x1;
+ imm10 = (offset >> 12) & 0x3ff;
+ imm11 = (offset >> 1) & 0x7ff;
+
+ j1 = (!i1) ^ s;
+ j2 = (!i2) ^ s;
+
+ first = 0xf000 | (s << 10) | imm10;
+ second = 0xd000 | (j1 << 13) | (j2 << 11) | imm11;
+
+ return (second << 16) | first;
+}
+#else
static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
{
long offset;
@@ -73,6 +105,7 @@ static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
return 0xeb000000 | offset;
}
+#endif
static int ftrace_modify_code(unsigned long pc, unsigned long old,
unsigned long new)
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index 319af1e..0218a1b 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -268,7 +268,7 @@ if ($arch eq "x86_64") {
} elsif ($arch eq "arm") {
$alignment = 2;
$section_type = '%progbits';
- $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_ARM_(CALL|PC24)" .
+ $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_ARM_(CALL|PC24|THM_CALL)" .
"\\s+(__gnu_mcount_nc|mcount)\$";
} elsif ($arch eq "ia64") {
--
1.7.0
I believe this is correct, but have you tested this on other archs other
than ARM? I can do it for x86 and PPC, but it will need to wait as those
machines are currently performing stress tests.
-- Steve
> endif
> +endif
>
> ifdef CONFIG_DEBUG_INFO
> KBUILD_CFLAGS += -g
--
> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> index 13e13d4..e10519f 100644
> --- a/kernel/trace/Kconfig
> +++ b/kernel/trace/Kconfig
> @@ -124,7 +124,7 @@ if FTRACE
> config FUNCTION_TRACER
> bool "Kernel Function Tracer"
> depends on HAVE_FUNCTION_TRACER
> - select FRAME_POINTER
> + select FRAME_POINTER if (!ARM_UNWIND)
I'm fine with this if the ARM maintainers are.
Acked-by: Steven Rostedt <ros...@goodmis.org>
-- Steve
> select KALLSYMS
> select GENERIC_TRACER
> select CONTEXT_SWITCH_TRACER
--
> diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
> index f3c9c0a..319af1e 100755
> --- a/scripts/recordmcount.pl
> +++ b/scripts/recordmcount.pl
> @@ -268,6 +268,8 @@ if ($arch eq "x86_64") {
> } elsif ($arch eq "arm") {
> $alignment = 2;
> $section_type = '%progbits';
> + $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_ARM_(CALL|PC24)" .
> + "\\s+(__gnu_mcount_nc|mcount)\$";
>
> } elsif ($arch eq "ia64") {
> $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$";
Acked-by: Steven Rostedt <ros...@goodmis.org>
-- Steve
--
Again, I'll have to test this on other archs, just to make sure its does
not cause any side effects. Oh, I forgot I now have a MIPS board I can
test on too. I'll probably do this on Monday.
Thanks,
-- Steve
> "$(if $(part-of-module),1,0)" "$(@)";
> endif
>
--
Again, I'll have to test this on other archs, just to make sure its does
not cause any side effects. Oh, I forgot I now have a MIPS board I can
test on too. I'll probably do this on Monday.
Thanks,
-- Steve
> "$(if $(part-of-module),1,0)" "$(@)";
> endif
>
--
I believe this is correct, but have you tested this on other archs other
than ARM? I can do it for x86 and PPC, but it will need to wait as those
machines are currently performing stress tests.
-- Steve
> endif
> +endif
>
> ifdef CONFIG_DEBUG_INFO
> KBUILD_CFLAGS += -g
--
> diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
> index f3c9c0a..319af1e 100755
> --- a/scripts/recordmcount.pl
> +++ b/scripts/recordmcount.pl
> @@ -268,6 +268,8 @@ if ($arch eq "x86_64") {
> } elsif ($arch eq "arm") {
> $alignment = 2;
> $section_type = '%progbits';
> + $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_ARM_(CALL|PC24)" .
> + "\\s+(__gnu_mcount_nc|mcount)\$";
>
> } elsif ($arch eq "ia64") {
> $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$";
Acked-by: Steven Rostedt <ros...@goodmis.org>
-- Steve
--
> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> index 13e13d4..e10519f 100644
> --- a/kernel/trace/Kconfig
> +++ b/kernel/trace/Kconfig
> @@ -124,7 +124,7 @@ if FTRACE
> config FUNCTION_TRACER
> bool "Kernel Function Tracer"
> depends on HAVE_FUNCTION_TRACER
> - select FRAME_POINTER
> + select FRAME_POINTER if (!ARM_UNWIND)
I'm fine with this if the ARM maintainers are.
Acked-by: Steven Rostedt <ros...@goodmis.org>
-- Steve
> select KALLSYMS
> select GENERIC_TRACER
> select CONTEXT_SWITCH_TRACER
--
I'm not familiar with ftrace but why does the called function returns
using "bx lr". Is this generated by the compiler? I had the impression
that if we don't enable interworking, we wouldn't get this instruction
(but haven't tried this yet).
--
Catalin
There's nothing special about the called function: it's just a regular C
function. GCC uses "bx lr" for the return.
Rabin
I've tested the series on x86-64.
Note that this particular change will not currently affect other archs
since they still have the "select FRAME_POINTER" in FUNCTION_TRACER.
Rabin
Rabin Vincent <ra...@rab.in> wrote:
> Fix the mcount routines to build and run on a kernel built with the
> Thumb-2 instruction set:
>
> - Without the BSYM, the following assembler errors appear:
>
> entry-common.S: Assembler messages:
> entry-common.S:179: Error: invalid immediate for address calculation (value = 0x00000004)
I'm still confused by this. I think that's a compiler problem but need
to get some feedback from toolchain people.
> --- a/arch/arm/kernel/entry-common.S
> +++ b/arch/arm/kernel/entry-common.S
> @@ -167,7 +167,8 @@ ENTRY(__gnu_mcount_nc)
> stmdb sp!, {r0-r3, lr}
> ldr r0, =ftrace_trace_function
> ldr r2, [r0]
> - adr r0, ftrace_stub
> + THUMB( orr r2, r2, #1 )
> + adr r0, BSYM(ftrace_stub)
If the ftrace_stub isn't .globl, the code compiles fine. My approach
would be something like this:
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index d085033..5f5aef6 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -122,7 +122,7 @@ ENTRY(__gnu_mcount_nc)
stmdb sp!, {r0-r3, lr}
ldr r0, =ftrace_trace_function
ldr r2, [r0]
- adr r0, ftrace_stub
+ adr r0, 1f
cmp r0, r2
bne gnu_trace
ldmia sp!, {r0-r3, ip, lr}
@@ -132,8 +132,9 @@ gnu_trace:
ldr r1, [sp, #20] @ lr of instrumented routine
mov r0, lr
sub r0, r0, #MCOUNT_INSN_SIZE
- mov lr, pc
+ adr lr, BSYM(2f)
mov pc, r2
+2:
ldmia sp!, {r0-r3, ip, lr}
mov pc, ip
@@ -141,7 +142,7 @@ ENTRY(mcount)
stmdb sp!, {r0-r3, lr}
ldr r0, =ftrace_trace_function
ldr r2, [r0]
- adr r0, ftrace_stub
+ adr r0, 1f
cmp r0, r2
bne trace
ldr lr, [fp, #-4] @ restore lr
@@ -160,6 +161,7 @@ trace:
.globl ftrace_stub
ftrace_stub:
+1:
mov pc, lr
#endif /* CONFIG_FUNCTION_TRACER */
--
Catalin
I think we did this already in other places, so:
Acked-by: Catalin Marinas <catalin...@arm.com>
New patch below:
From bf828a0c069b1bb3f6bf4e68f1dceecab396c286 Mon Sep 17 00:00:00 2001
From: Rabin Vincent <ra...@rab.in>
Date: Sun, 14 Feb 2010 01:18:34 +0530
Subject: [PATCH 06/10] ARM: ftrace: add Thumb-2 support
Fix the mcount routines to build and run on a kernel built with the
Thumb-2 instruction set by correcting the following errors using the
fixes suggested by Catalin Marinas:
- Problem: The following assembler errors appear at the "adr r0,
ftrace_stub" instruction:
entry-common.S: Assembler messages:
entry-common.S:179: Error: invalid immediate for address calculation (value = 0x00000004)
Fix: The errors don't occur with a non-global symbol, so use one.
- Problem: The "mov lr, pc" does not set the lsb when storing the pc in
lr. The called function returns with "bx lr", and the mode changes
to ARM.
Fix: Add a label on the return address and use "adr lr, BSYM(label)".
We don't modify the old mcount because it won't be built when using
Thumb-2.
Cc: Catalin Marinas <catalin...@arm.com>
Signed-off-by: Rabin Vincent <ra...@rab.in>
---
arch/arm/kernel/entry-common.S | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index c7a8c20..f5e75de 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -167,7 +167,7 @@ ENTRY(__gnu_mcount_nc)
stmdb sp!, {r0-r3, lr}
ldr r0, =ftrace_trace_function
ldr r2, [r0]
- adr r0, ftrace_stub
+ adr r0, .Lftrace_stub
cmp r0, r2
bne gnu_trace
ldmia sp!, {r0-r3, ip, lr}
@@ -177,8 +177,9 @@ gnu_trace:
ldr r1, [sp, #20] @ lr of instrumented routine
mov r0, lr
sub r0, r0, #MCOUNT_INSN_SIZE
- mov lr, pc
+ adr lr, BSYM(1f)
mov pc, r2
+1:
ldmia sp!, {r0-r3, ip, lr}
mov pc, ip
ENDPROC(__gnu_mcount_nc)
@@ -213,6 +214,7 @@ ENDPROC(mcount)
#endif /* CONFIG_DYNAMIC_FTRACE */
ENTRY(ftrace_stub)
+.Lftrace_stub:
mov pc, lr
ENDPROC(ftrace_stub)
--
1.7.0
I've now built PowerPC, SH, and MIPS kernels with dynamic ftrace enabled
and checked that the obj files don't change as the result of this
series. I've run the MIPS kernel on QEMU and x86_64 on my PC.
Rabin
On Sat, Mar 13, 2010 at 12:19:37PM +0530, Rabin Vincent wrote:
> This series contains fixes and improvements to the ARM ftrace support. It adds
> Thumb-2 support and re-implements the dynamic ftrace support.
Do you have any further concerns about this patchset?
If not, do you mind if I try to send in the series (v2 + the two updated
patches) via the ARM patch system, since most of the patches only touch
code under arch/arm/?
Rabin
Acked-by: Catalin Marinas <catalin...@arm.com>
--
Catalin
I'd like to resurrect this old ARM dynamic ftrace patchset. It still
applies on next except for a small merge conflict in one of the
ARM-specific patches.
On Sat, Mar 13, 2010 at 12:19:44PM +0530, Rabin Vincent wrote:
> On ARM, we have two ABIs, and the ABI used is controlled via a config
> option. Object files built with one ABI can't be merged with object
> files built with the other ABI. So, record_mcount.pl needs to use the
> same compiler flags as the kernel when generating the object file with
> the mcount locations. Ensure this by passing CFLAGS to the script.
>
> Signed-off-by: Rabin Vincent <ra...@rab.in>
This is the only non-arch/arm/ patch in the series which doesn't have
either of your Acked-bys, so if you'd be willing to ack it, I'll send in
the lot via rmk's ARM patch system for .37.
Alternatively, this one and "[PATCH 03/10] ftrace: allow building
without frame pointers" could go in via the tracing tree for .36? Then
I'll send in the ARM specific stuff for .37 via the ARM tree.
Thanks,
Rabin
I'll let Steve handling this one as I have few skills with things that touch
scripts/recordmcount.pl :)
Hmm, this changes the number of parameters passed to the
recordmcount.pl. shouldn't this be part of the change to
recordmcount.pl? Otherwise, we can break a bisect.
-- Steve
No, this doesn't change the number of parameters. KBUILD_CFLAGS
is included inside the CC argument.
Rabin
Argh! We had this discussion before. I must be getting old.
Acked-by: Steven Rostedt <ros...@goodmis.org>
-- Steve
"Rabin Vincent" <ra...@rab.in> wrote:
>On Sat, Aug 7, 2010 at 1:53 AM, Steven Rostedt <ros...@goodmis.org> wrote:
>>> > "$(if $(CONFIG_CPU_BIG_ENDIAN),big,little)" \
>>> > "$(if $(CONFIG_64BIT),64,32)" \
>>> > - "$(OBJDUMP)" "$(OBJCOPY)" "$(CC)" "$(LD)" "$(NM)" "$(RM)" "$(MV)" \
>>> > + "$(OBJDUMP)" "$(OBJCOPY)" "$(CC) $(KBUILD_CFLAGS)" \
>>> > + "$(LD)" "$(NM)" "$(RM)" "$(MV)" \
>>
>> Hmm, this changes the number of parameters passed to the
>> recordmcount.pl. shouldn't this be part of the change to
>> recordmcount.pl? Otherwise, we can break a bisect.
>
>No, this doesn't change the number of parameters. KBUILD_CFLAGS
>is included inside the CC argument.
>
>Rabin
--
Sent from my Android phone with K-9 Mail. Please excuse my brevity.