Commit 83cf1eec authored by Russell King's avatar Russell King

Merge branch 'ftrace' of git://github.com/rabinv/linux-2.6 into devel-stable

parents f1690d17 0e341af8
...@@ -14,6 +14,7 @@ config ARM ...@@ -14,6 +14,7 @@ config ARM
select HAVE_FUNCTION_TRACER if (!XIP_KERNEL) select HAVE_FUNCTION_TRACER if (!XIP_KERNEL)
select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL) select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL)
select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL) select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL)
select HAVE_FUNCTION_GRAPH_TRACER if (!THUMB2_KERNEL)
select HAVE_GENERIC_DMA_COHERENT select HAVE_GENERIC_DMA_COHERENT
select HAVE_KERNEL_GZIP select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZO select HAVE_KERNEL_LZO
......
...@@ -23,7 +23,7 @@ config STRICT_DEVMEM ...@@ -23,7 +23,7 @@ config STRICT_DEVMEM
config FRAME_POINTER config FRAME_POINTER
bool bool
depends on !THUMB2_KERNEL depends on !THUMB2_KERNEL
default y if !ARM_UNWIND default y if !ARM_UNWIND || FUNCTION_GRAPH_TRACER
help help
If you say N here, the resulting kernel will be slightly smaller and If you say N here, the resulting kernel will be slightly smaller and
faster. However, if neither FRAME_POINTER nor ARM_UNWIND are enabled, faster. However, if neither FRAME_POINTER nor ARM_UNWIND are enabled,
......
...@@ -63,6 +63,11 @@ ...@@ -63,6 +63,11 @@
#include <asm/outercache.h> #include <asm/outercache.h>
#define __exception __attribute__((section(".exception.text"))) #define __exception __attribute__((section(".exception.text")))
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
#define __exception_irq_entry __irq_entry
#else
#define __exception_irq_entry __exception
#endif
struct thread_info; struct thread_info;
struct task_struct; struct task_struct;
......
...@@ -15,13 +15,32 @@ struct undef_hook { ...@@ -15,13 +15,32 @@ struct undef_hook {
void register_undef_hook(struct undef_hook *hook); void register_undef_hook(struct undef_hook *hook);
void unregister_undef_hook(struct undef_hook *hook); void unregister_undef_hook(struct undef_hook *hook);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
static inline int __in_irqentry_text(unsigned long ptr)
{
extern char __irqentry_text_start[];
extern char __irqentry_text_end[];
return ptr >= (unsigned long)&__irqentry_text_start &&
ptr < (unsigned long)&__irqentry_text_end;
}
#else
static inline int __in_irqentry_text(unsigned long ptr)
{
return 0;
}
#endif
static inline int in_exception_text(unsigned long ptr) static inline int in_exception_text(unsigned long ptr)
{ {
extern char __exception_text_start[]; extern char __exception_text_start[];
extern char __exception_text_end[]; extern char __exception_text_end[];
int in;
in = ptr >= (unsigned long)&__exception_text_start &&
ptr < (unsigned long)&__exception_text_end;
return ptr >= (unsigned long)&__exception_text_start && return in ? : __in_irqentry_text(ptr);
ptr < (unsigned long)&__exception_text_end;
} }
extern void __init early_trap_init(void); extern void __init early_trap_init(void);
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET)
AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
ifdef CONFIG_DYNAMIC_FTRACE ifdef CONFIG_FUNCTION_TRACER
CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_ftrace.o = -pg
endif endif
...@@ -33,6 +33,7 @@ obj-$(CONFIG_SMP) += smp.o ...@@ -33,6 +33,7 @@ obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o
obj-$(CONFIG_ATAGS_PROC) += atags.o obj-$(CONFIG_ATAGS_PROC) += atags.o
......
...@@ -141,98 +141,170 @@ ENDPROC(ret_from_fork) ...@@ -141,98 +141,170 @@ ENDPROC(ret_from_fork)
#endif #endif
#endif #endif
#ifdef CONFIG_DYNAMIC_FTRACE .macro __mcount suffix
ENTRY(__gnu_mcount_nc) mcount_enter
mov ip, lr ldr r0, =ftrace_trace_function
ldmia sp!, {lr} ldr r2, [r0]
mov pc, ip adr r0, .Lftrace_stub
ENDPROC(__gnu_mcount_nc) cmp r0, r2
bne 1f
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
ldr r1, =ftrace_graph_return
ldr r2, [r1]
cmp r0, r2
bne ftrace_graph_caller\suffix
ldr r1, =ftrace_graph_entry
ldr r2, [r1]
ldr r0, =ftrace_graph_entry_stub
cmp r0, r2
bne ftrace_graph_caller\suffix
#endif
ENTRY(ftrace_caller) mcount_exit
stmdb sp!, {r0-r3, lr}
mov r0, lr 1: mcount_get_lr r1 @ lr of instrumented func
mov r0, lr @ instrumented function
sub r0, r0, #MCOUNT_INSN_SIZE
adr lr, BSYM(2f)
mov pc, r2
2: mcount_exit
.endm
.macro __ftrace_caller suffix
mcount_enter
mcount_get_lr r1 @ lr of instrumented func
mov r0, lr @ instrumented function
sub r0, r0, #MCOUNT_INSN_SIZE sub r0, r0, #MCOUNT_INSN_SIZE
ldr r1, [sp, #20]
.global ftrace_call .globl ftrace_call\suffix
ftrace_call: ftrace_call\suffix:
bl ftrace_stub bl ftrace_stub
ldmia sp!, {r0-r3, ip, lr}
mov pc, ip #ifdef CONFIG_FUNCTION_GRAPH_TRACER
ENDPROC(ftrace_caller) .globl ftrace_graph_call\suffix
ftrace_graph_call\suffix:
mov r0, r0
#endif
mcount_exit
.endm
.macro __ftrace_graph_caller
sub r0, fp, #4 @ &lr of instrumented routine (&parent)
#ifdef CONFIG_DYNAMIC_FTRACE
@ called from __ftrace_caller, saved in mcount_enter
ldr r1, [sp, #16] @ instrumented routine (func)
#else
@ called from __mcount, untouched in lr
mov r1, lr @ instrumented routine (func)
#endif
sub r1, r1, #MCOUNT_INSN_SIZE
mov r2, fp @ frame pointer
bl prepare_ftrace_return
mcount_exit
.endm
#ifdef CONFIG_OLD_MCOUNT #ifdef CONFIG_OLD_MCOUNT
/*
* mcount
*/
.macro mcount_enter
stmdb sp!, {r0-r3, lr}
.endm
.macro mcount_get_lr reg
ldr \reg, [fp, #-4]
.endm
.macro mcount_exit
ldr lr, [fp, #-4]
ldmia sp!, {r0-r3, pc}
.endm
ENTRY(mcount) ENTRY(mcount)
#ifdef CONFIG_DYNAMIC_FTRACE
stmdb sp!, {lr} stmdb sp!, {lr}
ldr lr, [fp, #-4] ldr lr, [fp, #-4]
ldmia sp!, {pc} ldmia sp!, {pc}
#else
__mcount _old
#endif
ENDPROC(mcount) ENDPROC(mcount)
#ifdef CONFIG_DYNAMIC_FTRACE
ENTRY(ftrace_caller_old) ENTRY(ftrace_caller_old)
stmdb sp!, {r0-r3, lr} __ftrace_caller _old
ldr r1, [fp, #-4]
mov r0, lr
sub r0, r0, #MCOUNT_INSN_SIZE
.globl ftrace_call_old
ftrace_call_old:
bl ftrace_stub
ldr lr, [fp, #-4] @ restore lr
ldmia sp!, {r0-r3, pc}
ENDPROC(ftrace_caller_old) ENDPROC(ftrace_caller_old)
#endif #endif
#else #ifdef CONFIG_FUNCTION_GRAPH_TRACER
ENTRY(ftrace_graph_caller_old)
__ftrace_graph_caller
ENDPROC(ftrace_graph_caller_old)
#endif
ENTRY(__gnu_mcount_nc) .purgem mcount_enter
.purgem mcount_get_lr
.purgem mcount_exit
#endif
/*
* __gnu_mcount_nc
*/
.macro mcount_enter
stmdb sp!, {r0-r3, lr} stmdb sp!, {r0-r3, lr}
ldr r0, =ftrace_trace_function .endm
ldr r2, [r0]
adr r0, .Lftrace_stub .macro mcount_get_lr reg
cmp r0, r2 ldr \reg, [sp, #20]
bne gnu_trace .endm
.macro mcount_exit
ldmia sp!, {r0-r3, ip, lr} ldmia sp!, {r0-r3, ip, lr}
mov pc, ip mov pc, ip
.endm
gnu_trace: ENTRY(__gnu_mcount_nc)
ldr r1, [sp, #20] @ lr of instrumented routine #ifdef CONFIG_DYNAMIC_FTRACE
mov r0, lr mov ip, lr
sub r0, r0, #MCOUNT_INSN_SIZE ldmia sp!, {lr}
adr lr, BSYM(1f)
mov pc, r2
1:
ldmia sp!, {r0-r3, ip, lr}
mov pc, ip mov pc, ip
#else
__mcount
#endif
ENDPROC(__gnu_mcount_nc) ENDPROC(__gnu_mcount_nc)
#ifdef CONFIG_OLD_MCOUNT #ifdef CONFIG_DYNAMIC_FTRACE
/* ENTRY(ftrace_caller)
* This is under an ifdef in order to force link-time errors for people trying __ftrace_caller
* to build with !FRAME_POINTER with a GCC which doesn't use the new-style ENDPROC(ftrace_caller)
* mcount. #endif
*/
ENTRY(mcount)
stmdb sp!, {r0-r3, lr}
ldr r0, =ftrace_trace_function
ldr r2, [r0]
adr r0, ftrace_stub
cmp r0, r2
bne trace
ldr lr, [fp, #-4] @ restore lr
ldmia sp!, {r0-r3, pc}
trace: #ifdef CONFIG_FUNCTION_GRAPH_TRACER
ldr r1, [fp, #-4] @ lr of instrumented routine ENTRY(ftrace_graph_caller)
mov r0, lr __ftrace_graph_caller
sub r0, r0, #MCOUNT_INSN_SIZE ENDPROC(ftrace_graph_caller)
mov lr, pc
mov pc, r2
ldr lr, [fp, #-4] @ restore lr
ldmia sp!, {r0-r3, pc}
ENDPROC(mcount)
#endif #endif
#endif /* CONFIG_DYNAMIC_FTRACE */ .purgem mcount_enter
.purgem mcount_get_lr
.purgem mcount_exit
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
.globl return_to_handler
return_to_handler:
stmdb sp!, {r0-r3}
mov r0, fp @ frame pointer
bl ftrace_return_to_handler
mov lr, r0 @ r0 has real ret addr
ldmia sp!, {r0-r3}
mov pc, lr
#endif
ENTRY(ftrace_stub) ENTRY(ftrace_stub)
.Lftrace_stub: .Lftrace_stub:
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#define NOP 0xe8bd4000 /* pop {lr} */ #define NOP 0xe8bd4000 /* pop {lr} */
#endif #endif
#ifdef CONFIG_DYNAMIC_FTRACE
#ifdef CONFIG_OLD_MCOUNT #ifdef CONFIG_OLD_MCOUNT
#define OLD_MCOUNT_ADDR ((unsigned long) mcount) #define OLD_MCOUNT_ADDR ((unsigned long) mcount)
#define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old) #define OLD_FTRACE_ADDR ((unsigned long) ftrace_caller_old)
...@@ -59,9 +60,9 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr) ...@@ -59,9 +60,9 @@ static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr)
} }
#endif #endif
/* construct a branch (BL) instruction to addr */
#ifdef CONFIG_THUMB2_KERNEL #ifdef CONFIG_THUMB2_KERNEL
static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr,
bool link)
{ {
unsigned long s, j1, j2, i1, i2, imm10, imm11; unsigned long s, j1, j2, i1, i2, imm10, imm11;
unsigned long first, second; unsigned long first, second;
...@@ -83,15 +84,22 @@ static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) ...@@ -83,15 +84,22 @@ static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
j2 = (!i2) ^ s; j2 = (!i2) ^ s;
first = 0xf000 | (s << 10) | imm10; first = 0xf000 | (s << 10) | imm10;
second = 0xd000 | (j1 << 13) | (j2 << 11) | imm11; second = 0x9000 | (j1 << 13) | (j2 << 11) | imm11;
if (link)
second |= 1 << 14;
return (second << 16) | first; return (second << 16) | first;
} }
#else #else
static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr,
bool link)
{ {
unsigned long opcode = 0xea000000;
long offset; long offset;
if (link)
opcode |= 1 << 24;
offset = (long)addr - (long)(pc + 8); offset = (long)addr - (long)(pc + 8);
if (unlikely(offset < -33554432 || offset > 33554428)) { if (unlikely(offset < -33554432 || offset > 33554428)) {
/* Can't generate branches that far (from ARM ARM). Ftrace /* Can't generate branches that far (from ARM ARM). Ftrace
...@@ -103,10 +111,15 @@ static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr) ...@@ -103,10 +111,15 @@ static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
offset = (offset >> 2) & 0x00ffffff; offset = (offset >> 2) & 0x00ffffff;
return 0xeb000000 | offset; return opcode | offset;
} }
#endif #endif
static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
{
return ftrace_gen_branch(pc, addr, true);
}
static int ftrace_modify_code(unsigned long pc, unsigned long old, static int ftrace_modify_code(unsigned long pc, unsigned long old,
unsigned long new) unsigned long new)
{ {
...@@ -193,3 +206,83 @@ int __init ftrace_dyn_arch_init(void *data) ...@@ -193,3 +206,83 @@ int __init ftrace_dyn_arch_init(void *data)
return 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)
{
unsigned long return_hooker = (unsigned long) &return_to_handler;
struct ftrace_graph_ent trace;
unsigned long old;
int err;
if (unlikely(atomic_read(&current->tracing_graph_pause)))
return;
old = *parent;
*parent = return_hooker;
err = ftrace_push_return_trace(old, self_addr, &trace.depth,
frame_pointer);
if (err == -EBUSY) {
*parent = old;
return;
}
trace.func = self_addr;
/* Only trace if the calling function expects to */
if (!ftrace_graph_entry(&trace)) {
current->curr_ret_stack--;
*parent = old;
}
}
#ifdef CONFIG_DYNAMIC_FTRACE
extern unsigned long ftrace_graph_call;
extern unsigned long ftrace_graph_call_old;
extern void ftrace_graph_caller_old(void);
static int __ftrace_modify_caller(unsigned long *callsite,
void (*func) (void), bool enable)
{
unsigned long caller_fn = (unsigned long) func;
unsigned long pc = (unsigned long) callsite;
unsigned long branch = ftrace_gen_branch(pc, caller_fn, false);
unsigned long nop = 0xe1a00000; /* mov r0, r0 */
unsigned long old = enable ? nop : branch;
unsigned long new = enable ? branch : nop;
return ftrace_modify_code(pc, old, new);
}
static int ftrace_modify_graph_caller(bool enable)
{
int ret;
ret = __ftrace_modify_caller(&ftrace_graph_call,
ftrace_graph_caller,
enable);
#ifdef CONFIG_OLD_MCOUNT
if (!ret)
ret = __ftrace_modify_caller(&ftrace_graph_call_old,
ftrace_graph_caller_old,
enable);
#endif
return ret;
}
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 */
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/ftrace.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/mach/irq.h> #include <asm/mach/irq.h>
...@@ -105,7 +106,8 @@ int show_interrupts(struct seq_file *p, void *v) ...@@ -105,7 +106,8 @@ int show_interrupts(struct seq_file *p, void *v)
* come via this function. Instead, they should provide their * come via this function. Instead, they should provide their
* own 'handler' * own 'handler'
*/ */
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{ {
struct pt_regs *old_regs = set_irq_regs(regs); struct pt_regs *old_regs = set_irq_regs(regs);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/cache.h> #include <linux/cache.h>
#include <linux/profile.h> #include <linux/profile.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/ftrace.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/cpu.h> #include <linux/cpu.h>
...@@ -457,7 +458,7 @@ static void ipi_timer(void) ...@@ -457,7 +458,7 @@ static void ipi_timer(void)
} }
#ifdef CONFIG_LOCAL_TIMERS #ifdef CONFIG_LOCAL_TIMERS
asmlinkage void __exception do_local_timer(struct pt_regs *regs) asmlinkage void __exception_irq_entry do_local_timer(struct pt_regs *regs)
{ {
struct pt_regs *old_regs = set_irq_regs(regs); struct pt_regs *old_regs = set_irq_regs(regs);
int cpu = smp_processor_id(); int cpu = smp_processor_id();
...@@ -544,7 +545,7 @@ static void ipi_cpu_stop(unsigned int cpu) ...@@ -544,7 +545,7 @@ static void ipi_cpu_stop(unsigned int cpu)
* *
* Bit 0 - Inter-processor function call * Bit 0 - Inter-processor function call
*/ */
asmlinkage void __exception do_IPI(struct pt_regs *regs) asmlinkage void __exception_irq_entry do_IPI(struct pt_regs *regs)
{ {
unsigned int cpu = smp_processor_id(); unsigned int cpu = smp_processor_id();
struct ipi_data *ipi = &per_cpu(ipi_data, cpu); struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
......
...@@ -101,6 +101,7 @@ SECTIONS ...@@ -101,6 +101,7 @@ SECTIONS
__exception_text_start = .; __exception_text_start = .;
*(.exception.text) *(.exception.text)
__exception_text_end = .; __exception_text_end = .;
IRQENTRY_TEXT
TEXT_TEXT TEXT_TEXT
SCHED_TEXT SCHED_TEXT
LOCK_TEXT LOCK_TEXT
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
*/ */
#include <linux/cnt32_to_63.h> #include <linux/cnt32_to_63.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/sched.h>
#include <asm/div64.h> #include <asm/div64.h>
#include <mach/hardware.h> #include <mach/hardware.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment