Commit 15d5b02c authored by Steven Rostedt (Red Hat)'s avatar Steven Rostedt (Red Hat) Committed by Steven Rostedt

ftrace/x86: Show trampoline call function in enabled_functions

The file /sys/kernel/debug/tracing/eneabled_functions is used to debug
ftrace function hooks. Add to the output what function is being called
by the trampoline if the arch supports it.

Add support for this feature in x86_64.

Cc: H. Peter Anvin <hpa@linux.intel.com>
Tested-by: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: default avatarJiri Kosina <jkosina@suse.cz>
Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
parent f3bea491
...@@ -48,7 +48,7 @@ int ftrace_arch_code_modify_post_process(void) ...@@ -48,7 +48,7 @@ int ftrace_arch_code_modify_post_process(void)
union ftrace_code_union { union ftrace_code_union {
char code[MCOUNT_INSN_SIZE]; char code[MCOUNT_INSN_SIZE];
struct { struct {
char e8; unsigned char e8;
int offset; int offset;
} __attribute__((packed)); } __attribute__((packed));
}; };
...@@ -797,12 +797,26 @@ static unsigned long create_trampoline(struct ftrace_ops *ops) ...@@ -797,12 +797,26 @@ static unsigned long create_trampoline(struct ftrace_ops *ops)
return (unsigned long)trampoline; return (unsigned long)trampoline;
} }
static unsigned long calc_trampoline_call_offset(bool save_regs)
{
unsigned long start_offset;
unsigned long call_offset;
if (save_regs) {
start_offset = (unsigned long)ftrace_regs_caller;
call_offset = (unsigned long)ftrace_regs_call;
} else {
start_offset = (unsigned long)ftrace_caller;
call_offset = (unsigned long)ftrace_call;
}
return call_offset - start_offset;
}
void arch_ftrace_update_trampoline(struct ftrace_ops *ops) void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
{ {
ftrace_func_t func; ftrace_func_t func;
unsigned char *new; unsigned char *new;
unsigned long start_offset;
unsigned long call_offset;
unsigned long offset; unsigned long offset;
unsigned long ip; unsigned long ip;
int ret; int ret;
...@@ -820,15 +834,7 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops) ...@@ -820,15 +834,7 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
return; return;
} }
if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) { offset = calc_trampoline_call_offset(ops->flags & FTRACE_OPS_FL_SAVE_REGS);
start_offset = (unsigned long)ftrace_regs_caller;
call_offset = (unsigned long)ftrace_regs_call;
} else {
start_offset = (unsigned long)ftrace_caller;
call_offset = (unsigned long)ftrace_call;
}
offset = call_offset - start_offset;
ip = ops->trampoline + offset; ip = ops->trampoline + offset;
func = ftrace_ops_get_func(ops); func = ftrace_ops_get_func(ops);
...@@ -840,6 +846,74 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops) ...@@ -840,6 +846,74 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
/* The update should never fail */ /* The update should never fail */
WARN_ON(ret); WARN_ON(ret);
} }
/* Return the address of the function the trampoline calls */
static void *addr_from_call(void *ptr)
{
union ftrace_code_union calc;
int ret;
ret = probe_kernel_read(&calc, ptr, MCOUNT_INSN_SIZE);
if (WARN_ON_ONCE(ret < 0))
return NULL;
/* Make sure this is a call */
if (WARN_ON_ONCE(calc.e8 != 0xe8)) {
pr_warn("Expected e8, got %x\n", calc.e8);
return NULL;
}
return ptr + MCOUNT_INSN_SIZE + calc.offset;
}
void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
unsigned long frame_pointer);
/*
* If the ops->trampoline was not allocated, then it probably
* has a static trampoline func, or is the ftrace caller itself.
*/
static void *static_tramp_func(struct ftrace_ops *ops, struct dyn_ftrace *rec)
{
unsigned long offset;
bool save_regs = rec->flags & FTRACE_FL_REGS_EN;
void *ptr;
if (ops && ops->trampoline) {
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
/*
* We only know about function graph tracer setting as static
* trampoline.
*/
if (ops->trampoline == FTRACE_GRAPH_ADDR)
return (void *)prepare_ftrace_return;
#endif
return NULL;
}
offset = calc_trampoline_call_offset(save_regs);
if (save_regs)
ptr = (void *)FTRACE_REGS_ADDR + offset;
else
ptr = (void *)FTRACE_ADDR + offset;
return addr_from_call(ptr);
}
void *arch_ftrace_trampoline_func(struct ftrace_ops *ops, struct dyn_ftrace *rec)
{
unsigned long offset;
/* If we didn't allocate this trampoline, consider it static */
if (!ops || !(ops->flags & FTRACE_OPS_FL_ALLOC_TRAMP))
return static_tramp_func(ops, rec);
offset = calc_trampoline_call_offset(ops->flags & FTRACE_OPS_FL_SAVE_REGS);
return addr_from_call((void *)ops->trampoline + offset);
}
#endif /* CONFIG_X86_64 */ #endif /* CONFIG_X86_64 */
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
......
...@@ -2952,6 +2952,22 @@ static void t_stop(struct seq_file *m, void *p) ...@@ -2952,6 +2952,22 @@ static void t_stop(struct seq_file *m, void *p)
mutex_unlock(&ftrace_lock); mutex_unlock(&ftrace_lock);
} }
void * __weak
arch_ftrace_trampoline_func(struct ftrace_ops *ops, struct dyn_ftrace *rec)
{
return NULL;
}
static void add_trampoline_func(struct seq_file *m, struct ftrace_ops *ops,
struct dyn_ftrace *rec)
{
void *ptr;
ptr = arch_ftrace_trampoline_func(ops, rec);
if (ptr)
seq_printf(m, " ->%pS", ptr);
}
static int t_show(struct seq_file *m, void *v) static int t_show(struct seq_file *m, void *v)
{ {
struct ftrace_iterator *iter = m->private; struct ftrace_iterator *iter = m->private;
...@@ -2975,19 +2991,21 @@ static int t_show(struct seq_file *m, void *v) ...@@ -2975,19 +2991,21 @@ static int t_show(struct seq_file *m, void *v)
seq_printf(m, "%ps", (void *)rec->ip); seq_printf(m, "%ps", (void *)rec->ip);
if (iter->flags & FTRACE_ITER_ENABLED) { if (iter->flags & FTRACE_ITER_ENABLED) {
struct ftrace_ops *ops = NULL;
seq_printf(m, " (%ld)%s", seq_printf(m, " (%ld)%s",
ftrace_rec_count(rec), ftrace_rec_count(rec),
rec->flags & FTRACE_FL_REGS ? " R" : " "); rec->flags & FTRACE_FL_REGS ? " R" : " ");
if (rec->flags & FTRACE_FL_TRAMP_EN) { if (rec->flags & FTRACE_FL_TRAMP_EN) {
struct ftrace_ops *ops;
ops = ftrace_find_tramp_ops_any(rec); ops = ftrace_find_tramp_ops_any(rec);
if (ops) if (ops)
seq_printf(m, "\ttramp: %pS", seq_printf(m, "\ttramp: %pS",
(void *)ops->trampoline); (void *)ops->trampoline);
else else
seq_printf(m, "\ttramp: ERROR!"); seq_printf(m, "\ttramp: ERROR!");
} }
add_trampoline_func(m, ops, rec);
} }
seq_printf(m, "\n"); seq_printf(m, "\n");
......
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