ARM: ftrace: graph tracer + dynamic ftrace
Support the graph tracer + dynamic ftrace combination on ARM. Signed-off-by: Rabin Vincent <rabin@rab.in>
This commit is contained in:
@@ -183,12 +183,24 @@ ENDPROC(ret_from_fork)
|
|||||||
ftrace_call\suffix:
|
ftrace_call\suffix:
|
||||||
bl ftrace_stub
|
bl ftrace_stub
|
||||||
|
|
||||||
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
|
.globl ftrace_graph_call\suffix
|
||||||
|
ftrace_graph_call\suffix:
|
||||||
|
mov r0, r0
|
||||||
|
#endif
|
||||||
|
|
||||||
mcount_exit
|
mcount_exit
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro __ftrace_graph_caller
|
.macro __ftrace_graph_caller
|
||||||
sub r0, fp, #4 @ &lr of instrumented routine (&parent)
|
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)
|
mov r1, lr @ instrumented routine (func)
|
||||||
|
#endif
|
||||||
sub r1, r1, #MCOUNT_INSN_SIZE
|
sub r1, r1, #MCOUNT_INSN_SIZE
|
||||||
mov r2, fp @ frame pointer
|
mov r2, fp @ frame pointer
|
||||||
bl prepare_ftrace_return
|
bl prepare_ftrace_return
|
||||||
|
@@ -60,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;
|
||||||
@@ -84,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
|
||||||
@@ -104,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)
|
||||||
{
|
{
|
||||||
@@ -226,4 +238,51 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
|
|||||||
*parent = old;
|
*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 */
|
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||||
|
Reference in New Issue
Block a user