mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									e59a0bff3e
								
							
						
					
					
						commit
						ced39002f5
					
				
					 5 changed files with 109 additions and 2 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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, | ||||
| 
 | ||||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Jiri Olsa
						Jiri Olsa