ftrace, perf: Add support to use function tracepoint in perf
Adding perf registration support for the ftrace function event, so it is now possible to register it via perf interface. The perf_event struct statically contains ftrace_ops as a handle for function tracer. The function tracer is registered/unregistered in open/close actions. To be efficient, we enable/disable ftrace_ops each time the traced process is scheduled in/out (via TRACE_REG_PERF_(ADD|DELL) handlers). This way tracing is enabled only when the process is running. Intentionally using this way instead of the event's hw state PERF_HES_STOPPED, which would not disable the ftrace_ops. It is now possible to use function trace within perf commands like: perf record -e ftrace:function ls perf stat -e ftrace:function ls Allowed only for root. Link: http://lkml.kernel.org/r/1329317514-8131-6-git-send-email-jolsa@redhat.com Acked-by: Frederic Weisbecker <fweisbec@gmail.com> Signed-off-by: Jiri Olsa <jolsa@redhat.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
Родитель
e59a0bff3e
Коммит
ced39002f5
|
@ -859,6 +859,9 @@ struct perf_event {
|
||||||
#ifdef CONFIG_EVENT_TRACING
|
#ifdef CONFIG_EVENT_TRACING
|
||||||
struct ftrace_event_call *tp_event;
|
struct ftrace_event_call *tp_event;
|
||||||
struct event_filter *filter;
|
struct event_filter *filter;
|
||||||
|
#ifdef CONFIG_FUNCTION_TRACER
|
||||||
|
struct ftrace_ops ftrace_ops;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_PERF
|
#ifdef CONFIG_CGROUP_PERF
|
||||||
|
|
|
@ -595,6 +595,8 @@ static inline int ftrace_trace_task(struct task_struct *task)
|
||||||
static inline int ftrace_is_dead(void) { return 0; }
|
static inline int ftrace_is_dead(void) { return 0; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int ftrace_event_is_function(struct ftrace_event_call *call);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* struct trace_parser - servers for reading the user input separated by spaces
|
* struct trace_parser - servers for reading the user input separated by spaces
|
||||||
* @cont: set if the input is not complete - no final space char was found
|
* @cont: set if the input is not complete - no final space char was found
|
||||||
|
@ -832,4 +834,13 @@ extern const char *__stop___trace_bprintk_fmt[];
|
||||||
FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print))
|
FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print))
|
||||||
#include "trace_entries.h"
|
#include "trace_entries.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_PERF_EVENTS
|
||||||
|
#ifdef CONFIG_FUNCTION_TRACER
|
||||||
|
int perf_ftrace_event_register(struct ftrace_event_call *call,
|
||||||
|
enum trace_reg type, void *data);
|
||||||
|
#else
|
||||||
|
#define perf_ftrace_event_register NULL
|
||||||
|
#endif /* CONFIG_FUNCTION_TRACER */
|
||||||
|
#endif /* CONFIG_PERF_EVENTS */
|
||||||
|
|
||||||
#endif /* _LINUX_KERNEL_TRACE_H */
|
#endif /* _LINUX_KERNEL_TRACE_H */
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
/*
|
/*
|
||||||
* Function trace entry - function address and parent function address:
|
* Function trace entry - function address and parent function address:
|
||||||
*/
|
*/
|
||||||
FTRACE_ENTRY(function, ftrace_entry,
|
FTRACE_ENTRY_REG(function, ftrace_entry,
|
||||||
|
|
||||||
TRACE_FN,
|
TRACE_FN,
|
||||||
|
|
||||||
|
@ -64,7 +64,9 @@ FTRACE_ENTRY(function, ftrace_entry,
|
||||||
__field( unsigned long, parent_ip )
|
__field( unsigned long, parent_ip )
|
||||||
),
|
),
|
||||||
|
|
||||||
F_printk(" %lx <-- %lx", __entry->ip, __entry->parent_ip)
|
F_printk(" %lx <-- %lx", __entry->ip, __entry->parent_ip),
|
||||||
|
|
||||||
|
perf_ftrace_event_register
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Function call entry */
|
/* Function call entry */
|
||||||
|
|
|
@ -24,6 +24,11 @@ static int total_ref_count;
|
||||||
static int perf_trace_event_perm(struct ftrace_event_call *tp_event,
|
static int perf_trace_event_perm(struct ftrace_event_call *tp_event,
|
||||||
struct perf_event *p_event)
|
struct perf_event *p_event)
|
||||||
{
|
{
|
||||||
|
/* The ftrace function trace is allowed only for root. */
|
||||||
|
if (ftrace_event_is_function(tp_event) &&
|
||||||
|
perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
/* No tracing, just counting, so no obvious leak */
|
/* No tracing, just counting, so no obvious leak */
|
||||||
if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW))
|
if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -250,3 +255,84 @@ __kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
|
||||||
return raw_data;
|
return raw_data;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);
|
EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);
|
||||||
|
|
||||||
|
#ifdef CONFIG_FUNCTION_TRACER
|
||||||
|
static void
|
||||||
|
perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip)
|
||||||
|
{
|
||||||
|
struct ftrace_entry *entry;
|
||||||
|
struct hlist_head *head;
|
||||||
|
struct pt_regs regs;
|
||||||
|
int rctx;
|
||||||
|
|
||||||
|
#define ENTRY_SIZE (ALIGN(sizeof(struct ftrace_entry) + sizeof(u32), \
|
||||||
|
sizeof(u64)) - sizeof(u32))
|
||||||
|
|
||||||
|
BUILD_BUG_ON(ENTRY_SIZE > PERF_MAX_TRACE_SIZE);
|
||||||
|
|
||||||
|
perf_fetch_caller_regs(®s);
|
||||||
|
|
||||||
|
entry = perf_trace_buf_prepare(ENTRY_SIZE, TRACE_FN, NULL, &rctx);
|
||||||
|
if (!entry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entry->ip = ip;
|
||||||
|
entry->parent_ip = parent_ip;
|
||||||
|
|
||||||
|
head = this_cpu_ptr(event_function.perf_events);
|
||||||
|
perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0,
|
||||||
|
1, ®s, head);
|
||||||
|
|
||||||
|
#undef ENTRY_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perf_ftrace_function_register(struct perf_event *event)
|
||||||
|
{
|
||||||
|
struct ftrace_ops *ops = &event->ftrace_ops;
|
||||||
|
|
||||||
|
ops->flags |= FTRACE_OPS_FL_CONTROL;
|
||||||
|
ops->func = perf_ftrace_function_call;
|
||||||
|
return register_ftrace_function(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perf_ftrace_function_unregister(struct perf_event *event)
|
||||||
|
{
|
||||||
|
struct ftrace_ops *ops = &event->ftrace_ops;
|
||||||
|
return unregister_ftrace_function(ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void perf_ftrace_function_enable(struct perf_event *event)
|
||||||
|
{
|
||||||
|
ftrace_function_local_enable(&event->ftrace_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void perf_ftrace_function_disable(struct perf_event *event)
|
||||||
|
{
|
||||||
|
ftrace_function_local_disable(&event->ftrace_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
int perf_ftrace_event_register(struct ftrace_event_call *call,
|
||||||
|
enum trace_reg type, void *data)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case TRACE_REG_REGISTER:
|
||||||
|
case TRACE_REG_UNREGISTER:
|
||||||
|
break;
|
||||||
|
case TRACE_REG_PERF_REGISTER:
|
||||||
|
case TRACE_REG_PERF_UNREGISTER:
|
||||||
|
return 0;
|
||||||
|
case TRACE_REG_PERF_OPEN:
|
||||||
|
return perf_ftrace_function_register(data);
|
||||||
|
case TRACE_REG_PERF_CLOSE:
|
||||||
|
return perf_ftrace_function_unregister(data);
|
||||||
|
case TRACE_REG_PERF_ADD:
|
||||||
|
perf_ftrace_function_enable(data);
|
||||||
|
return 0;
|
||||||
|
case TRACE_REG_PERF_DEL:
|
||||||
|
perf_ftrace_function_disable(data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_FUNCTION_TRACER */
|
||||||
|
|
|
@ -184,4 +184,9 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call;
|
||||||
FTRACE_ENTRY_REG(call, struct_name, etype, \
|
FTRACE_ENTRY_REG(call, struct_name, etype, \
|
||||||
PARAMS(tstruct), PARAMS(print), NULL)
|
PARAMS(tstruct), PARAMS(print), NULL)
|
||||||
|
|
||||||
|
int ftrace_event_is_function(struct ftrace_event_call *call)
|
||||||
|
{
|
||||||
|
return call == &event_function;
|
||||||
|
}
|
||||||
|
|
||||||
#include "trace_entries.h"
|
#include "trace_entries.h"
|
||||||
|
|
Загрузка…
Ссылка в новой задаче