Skip to content

Commit

Permalink
ftrace, perf: Add support to use function tracepoint in perf
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jiri Olsa authored and Steven Rostedt committed Feb 21, 2012
1 parent e59a0bf commit ced3900
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 2 deletions.
3 changes: 3 additions & 0 deletions include/linux/perf_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,9 @@ struct perf_event {
#ifdef CONFIG_EVENT_TRACING
struct ftrace_event_call *tp_event;
struct event_filter *filter;
#ifdef CONFIG_FUNCTION_TRACER
struct ftrace_ops ftrace_ops;
#endif
#endif

#ifdef CONFIG_CGROUP_PERF
Expand Down
11 changes: 11 additions & 0 deletions kernel/trace/trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,8 @@ static inline int ftrace_trace_task(struct task_struct *task)
static inline int ftrace_is_dead(void) { return 0; }
#endif

int ftrace_event_is_function(struct ftrace_event_call *call);

/*
* 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
Expand Down Expand Up @@ -832,4 +834,13 @@ extern const char *__stop___trace_bprintk_fmt[];
FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print))
#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 */
6 changes: 4 additions & 2 deletions kernel/trace/trace_entries.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
/*
* Function trace entry - function address and parent function address:
*/
FTRACE_ENTRY(function, ftrace_entry,
FTRACE_ENTRY_REG(function, ftrace_entry,

TRACE_FN,

Expand All @@ -64,7 +64,9 @@ FTRACE_ENTRY(function, ftrace_entry,
__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 */
Expand Down
86 changes: 86 additions & 0 deletions kernel/trace/trace_event_perf.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ static int total_ref_count;
static int perf_trace_event_perm(struct ftrace_event_call *tp_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 */
if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW))
return 0;
Expand Down Expand Up @@ -250,3 +255,84 @@ __kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
return raw_data;
}
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(&regs);

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, &regs, 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 */
5 changes: 5 additions & 0 deletions kernel/trace/trace_export.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,9 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call;
FTRACE_ENTRY_REG(call, struct_name, etype, \
PARAMS(tstruct), PARAMS(print), NULL)

int ftrace_event_is_function(struct ftrace_event_call *call)
{
return call == &event_function;
}

#include "trace_entries.h"

0 comments on commit ced3900

Please sign in to comment.