forked from mirrors/linux
		
	perf, bpf: Introduce PERF_RECORD_KSYMBOL
For better performance analysis of dynamically JITed and loaded kernel
functions, such as BPF programs, this patch introduces
PERF_RECORD_KSYMBOL, a new perf_event_type that exposes kernel symbol
register/unregister information to user space.
The following data structure is used for PERF_RECORD_KSYMBOL.
    /*
     * struct {
     *      struct perf_event_header        header;
     *      u64                             addr;
     *      u32                             len;
     *      u16                             ksym_type;
     *      u16                             flags;
     *      char                            name[];
     *      struct sample_id                sample_id;
     * };
     */
Signed-off-by: Song Liu <songliubraving@fb.com>
Reviewed-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Acked-by: Peter Zijlstra <peterz@infradead.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: kernel-team@fb.com
Cc: netdev@vger.kernel.org
Link: http://lkml.kernel.org/r/20190117161521.1341602-2-songliubraving@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									5620196951
								
							
						
					
					
						commit
						76193a9452
					
				
					 3 changed files with 130 additions and 2 deletions
				
			
		|  | @ -1122,6 +1122,10 @@ static inline void perf_event_task_sched_out(struct task_struct *prev, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern void perf_event_mmap(struct vm_area_struct *vma); | extern void perf_event_mmap(struct vm_area_struct *vma); | ||||||
|  | 
 | ||||||
|  | extern void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, | ||||||
|  | 			       bool unregister, const char *sym); | ||||||
|  | 
 | ||||||
| extern struct perf_guest_info_callbacks *perf_guest_cbs; | extern struct perf_guest_info_callbacks *perf_guest_cbs; | ||||||
| extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); | extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); | ||||||
| extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); | extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); | ||||||
|  | @ -1342,6 +1346,10 @@ static inline int perf_unregister_guest_info_callbacks | ||||||
| (struct perf_guest_info_callbacks *callbacks)				{ return 0; } | (struct perf_guest_info_callbacks *callbacks)				{ return 0; } | ||||||
| 
 | 
 | ||||||
| static inline void perf_event_mmap(struct vm_area_struct *vma)		{ } | static inline void perf_event_mmap(struct vm_area_struct *vma)		{ } | ||||||
|  | 
 | ||||||
|  | typedef int (perf_ksymbol_get_name_f)(char *name, int name_len, void *data); | ||||||
|  | static inline void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, | ||||||
|  | 				      bool unregister, const char *sym)	{ } | ||||||
| static inline void perf_event_exec(void)				{ } | static inline void perf_event_exec(void)				{ } | ||||||
| static inline void perf_event_comm(struct task_struct *tsk, bool exec)	{ } | static inline void perf_event_comm(struct task_struct *tsk, bool exec)	{ } | ||||||
| static inline void perf_event_namespaces(struct task_struct *tsk)	{ } | static inline void perf_event_namespaces(struct task_struct *tsk)	{ } | ||||||
|  |  | ||||||
|  | @ -372,7 +372,8 @@ struct perf_event_attr { | ||||||
| 				context_switch :  1, /* context switch data */ | 				context_switch :  1, /* context switch data */ | ||||||
| 				write_backward :  1, /* Write ring buffer from end to beginning */ | 				write_backward :  1, /* Write ring buffer from end to beginning */ | ||||||
| 				namespaces     :  1, /* include namespaces data */ | 				namespaces     :  1, /* include namespaces data */ | ||||||
| 				__reserved_1   : 35; | 				ksymbol        :  1, /* include ksymbol events */ | ||||||
|  | 				__reserved_1   : 34; | ||||||
| 
 | 
 | ||||||
| 	union { | 	union { | ||||||
| 		__u32		wakeup_events;	  /* wakeup every n events */ | 		__u32		wakeup_events;	  /* wakeup every n events */ | ||||||
|  | @ -963,9 +964,32 @@ enum perf_event_type { | ||||||
| 	 */ | 	 */ | ||||||
| 	PERF_RECORD_NAMESPACES			= 16, | 	PERF_RECORD_NAMESPACES			= 16, | ||||||
| 
 | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Record ksymbol register/unregister events: | ||||||
|  | 	 * | ||||||
|  | 	 * struct { | ||||||
|  | 	 *	struct perf_event_header	header; | ||||||
|  | 	 *	u64				addr; | ||||||
|  | 	 *	u32				len; | ||||||
|  | 	 *	u16				ksym_type; | ||||||
|  | 	 *	u16				flags; | ||||||
|  | 	 *	char				name[]; | ||||||
|  | 	 *	struct sample_id		sample_id; | ||||||
|  | 	 * }; | ||||||
|  | 	 */ | ||||||
|  | 	PERF_RECORD_KSYMBOL			= 17, | ||||||
|  | 
 | ||||||
| 	PERF_RECORD_MAX,			/* non-ABI */ | 	PERF_RECORD_MAX,			/* non-ABI */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | enum perf_record_ksymbol_type { | ||||||
|  | 	PERF_RECORD_KSYMBOL_TYPE_UNKNOWN	= 0, | ||||||
|  | 	PERF_RECORD_KSYMBOL_TYPE_BPF		= 1, | ||||||
|  | 	PERF_RECORD_KSYMBOL_TYPE_MAX		/* non-ABI */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER	(1 << 0) | ||||||
|  | 
 | ||||||
| #define PERF_MAX_STACK_DEPTH		127 | #define PERF_MAX_STACK_DEPTH		127 | ||||||
| #define PERF_MAX_CONTEXTS_PER_STACK	  8 | #define PERF_MAX_CONTEXTS_PER_STACK	  8 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -385,6 +385,7 @@ static atomic_t nr_namespaces_events __read_mostly; | ||||||
| static atomic_t nr_task_events __read_mostly; | static atomic_t nr_task_events __read_mostly; | ||||||
| static atomic_t nr_freq_events __read_mostly; | static atomic_t nr_freq_events __read_mostly; | ||||||
| static atomic_t nr_switch_events __read_mostly; | static atomic_t nr_switch_events __read_mostly; | ||||||
|  | static atomic_t nr_ksymbol_events __read_mostly; | ||||||
| 
 | 
 | ||||||
| static LIST_HEAD(pmus); | static LIST_HEAD(pmus); | ||||||
| static DEFINE_MUTEX(pmus_lock); | static DEFINE_MUTEX(pmus_lock); | ||||||
|  | @ -4235,7 +4236,7 @@ static bool is_sb_event(struct perf_event *event) | ||||||
| 
 | 
 | ||||||
| 	if (attr->mmap || attr->mmap_data || attr->mmap2 || | 	if (attr->mmap || attr->mmap_data || attr->mmap2 || | ||||||
| 	    attr->comm || attr->comm_exec || | 	    attr->comm || attr->comm_exec || | ||||||
| 	    attr->task || | 	    attr->task || attr->ksymbol || | ||||||
| 	    attr->context_switch) | 	    attr->context_switch) | ||||||
| 		return true; | 		return true; | ||||||
| 	return false; | 	return false; | ||||||
|  | @ -4305,6 +4306,8 @@ static void unaccount_event(struct perf_event *event) | ||||||
| 		dec = true; | 		dec = true; | ||||||
| 	if (has_branch_stack(event)) | 	if (has_branch_stack(event)) | ||||||
| 		dec = true; | 		dec = true; | ||||||
|  | 	if (event->attr.ksymbol) | ||||||
|  | 		atomic_dec(&nr_ksymbol_events); | ||||||
| 
 | 
 | ||||||
| 	if (dec) { | 	if (dec) { | ||||||
| 		if (!atomic_add_unless(&perf_sched_count, -1, 1)) | 		if (!atomic_add_unless(&perf_sched_count, -1, 1)) | ||||||
|  | @ -7653,6 +7656,97 @@ static void perf_log_throttle(struct perf_event *event, int enable) | ||||||
| 	perf_output_end(&handle); | 	perf_output_end(&handle); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * ksymbol register/unregister tracking | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | struct perf_ksymbol_event { | ||||||
|  | 	const char	*name; | ||||||
|  | 	int		name_len; | ||||||
|  | 	struct { | ||||||
|  | 		struct perf_event_header        header; | ||||||
|  | 		u64				addr; | ||||||
|  | 		u32				len; | ||||||
|  | 		u16				ksym_type; | ||||||
|  | 		u16				flags; | ||||||
|  | 	} event_id; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int perf_event_ksymbol_match(struct perf_event *event) | ||||||
|  | { | ||||||
|  | 	return event->attr.ksymbol; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void perf_event_ksymbol_output(struct perf_event *event, void *data) | ||||||
|  | { | ||||||
|  | 	struct perf_ksymbol_event *ksymbol_event = data; | ||||||
|  | 	struct perf_output_handle handle; | ||||||
|  | 	struct perf_sample_data sample; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	if (!perf_event_ksymbol_match(event)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	perf_event_header__init_id(&ksymbol_event->event_id.header, | ||||||
|  | 				   &sample, event); | ||||||
|  | 	ret = perf_output_begin(&handle, event, | ||||||
|  | 				ksymbol_event->event_id.header.size); | ||||||
|  | 	if (ret) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	perf_output_put(&handle, ksymbol_event->event_id); | ||||||
|  | 	__output_copy(&handle, ksymbol_event->name, ksymbol_event->name_len); | ||||||
|  | 	perf_event__output_id_sample(event, &handle, &sample); | ||||||
|  | 
 | ||||||
|  | 	perf_output_end(&handle); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, bool unregister, | ||||||
|  | 			const char *sym) | ||||||
|  | { | ||||||
|  | 	struct perf_ksymbol_event ksymbol_event; | ||||||
|  | 	char name[KSYM_NAME_LEN]; | ||||||
|  | 	u16 flags = 0; | ||||||
|  | 	int name_len; | ||||||
|  | 
 | ||||||
|  | 	if (!atomic_read(&nr_ksymbol_events)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (ksym_type >= PERF_RECORD_KSYMBOL_TYPE_MAX || | ||||||
|  | 	    ksym_type == PERF_RECORD_KSYMBOL_TYPE_UNKNOWN) | ||||||
|  | 		goto err; | ||||||
|  | 
 | ||||||
|  | 	strlcpy(name, sym, KSYM_NAME_LEN); | ||||||
|  | 	name_len = strlen(name) + 1; | ||||||
|  | 	while (!IS_ALIGNED(name_len, sizeof(u64))) | ||||||
|  | 		name[name_len++] = '\0'; | ||||||
|  | 	BUILD_BUG_ON(KSYM_NAME_LEN % sizeof(u64)); | ||||||
|  | 
 | ||||||
|  | 	if (unregister) | ||||||
|  | 		flags |= PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER; | ||||||
|  | 
 | ||||||
|  | 	ksymbol_event = (struct perf_ksymbol_event){ | ||||||
|  | 		.name = name, | ||||||
|  | 		.name_len = name_len, | ||||||
|  | 		.event_id = { | ||||||
|  | 			.header = { | ||||||
|  | 				.type = PERF_RECORD_KSYMBOL, | ||||||
|  | 				.size = sizeof(ksymbol_event.event_id) + | ||||||
|  | 					name_len, | ||||||
|  | 			}, | ||||||
|  | 			.addr = addr, | ||||||
|  | 			.len = len, | ||||||
|  | 			.ksym_type = ksym_type, | ||||||
|  | 			.flags = flags, | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	perf_iterate_sb(perf_event_ksymbol_output, &ksymbol_event, NULL); | ||||||
|  | 	return; | ||||||
|  | err: | ||||||
|  | 	WARN_ONCE(1, "%s: Invalid KSYMBOL type 0x%x\n", __func__, ksym_type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void perf_event_itrace_started(struct perf_event *event) | void perf_event_itrace_started(struct perf_event *event) | ||||||
| { | { | ||||||
| 	event->attach_state |= PERF_ATTACH_ITRACE; | 	event->attach_state |= PERF_ATTACH_ITRACE; | ||||||
|  | @ -9912,6 +10006,8 @@ static void account_event(struct perf_event *event) | ||||||
| 		inc = true; | 		inc = true; | ||||||
| 	if (is_cgroup_event(event)) | 	if (is_cgroup_event(event)) | ||||||
| 		inc = true; | 		inc = true; | ||||||
|  | 	if (event->attr.ksymbol) | ||||||
|  | 		atomic_inc(&nr_ksymbol_events); | ||||||
| 
 | 
 | ||||||
| 	if (inc) { | 	if (inc) { | ||||||
| 		/*
 | 		/*
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Song Liu
						Song Liu