diff options
| author | Jiri Olsa <jolsa@redhat.com> | 2012-02-15 15:51:52 +0100 | 
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2012-02-21 11:08:27 -0500 | 
| commit | ced39002f5ea736b716ae233fb68b26d59783912 (patch) | |
| tree | 4942ffe3b9e26d5122a5febb885d0b541e3aea33 /kernel/trace/trace_event_perf.c | |
| parent | e59a0bff3ecf389951e3c9378ddfd00f6448bfaa (diff) | |
| download | olio-linux-3.10-ced39002f5ea736b716ae233fb68b26d59783912.tar.xz olio-linux-3.10-ced39002f5ea736b716ae233fb68b26d59783912.zip  | |
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>
Diffstat (limited to 'kernel/trace/trace_event_perf.c')
| -rw-r--r-- | kernel/trace/trace_event_perf.c | 86 | 
1 files changed, 86 insertions, 0 deletions
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index d72af0b0382..fdeeb5c4962 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -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; @@ -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(®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 */  |