forked from mirrors/linux
		
	perf/ftrace: Fix double traces of perf on ftrace:function
When running perf on the ftrace:function tracepoint, there is a bug
which can be reproduced by:
  perf record -e ftrace:function -a sleep 20 &
  perf record -e ftrace:function ls
  perf script
              ls 10304 [005]   171.853235: ftrace:function:
  perf_output_begin
              ls 10304 [005]   171.853237: ftrace:function:
  perf_output_begin
              ls 10304 [005]   171.853239: ftrace:function:
  task_tgid_nr_ns
              ls 10304 [005]   171.853240: ftrace:function:
  task_tgid_nr_ns
              ls 10304 [005]   171.853242: ftrace:function:
  __task_pid_nr_ns
              ls 10304 [005]   171.853244: ftrace:function:
  __task_pid_nr_ns
We can see that all the function traces are doubled.
The problem is caused by the inconsistency of the register
function perf_ftrace_event_register() with the probe function
perf_ftrace_function_call(). The former registers one probe
for every perf_event. And the latter handles all perf_events
on the current cpu. So when two perf_events on the current cpu,
the traces of them will be doubled.
So this patch adds an extra parameter "event" for perf_tp_event,
only send sample data to this event when it's not NULL.
Signed-off-by: Zhou Chengming <zhouchengming1@huawei.com>
Reviewed-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: acme@kernel.org
Cc: alexander.shishkin@linux.intel.com
Cc: huawei.libin@huawei.com
Link: http://lkml.kernel.org/r/1503668977-12526-1-git-send-email-zhouchengming1@huawei.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
			
			
This commit is contained in:
		
							parent
							
								
									f12f42acdb
								
							
						
					
					
						commit
						75e8387685
					
				
					 7 changed files with 20 additions and 13 deletions
				
			
		|  | @ -1201,7 +1201,7 @@ extern void perf_event_init(void); | |||
| extern void perf_tp_event(u16 event_type, u64 count, void *record, | ||||
| 			  int entry_size, struct pt_regs *regs, | ||||
| 			  struct hlist_head *head, int rctx, | ||||
| 			  struct task_struct *task); | ||||
| 			  struct task_struct *task, struct perf_event *event); | ||||
| extern void perf_bp_event(struct perf_event *event, void *data); | ||||
| 
 | ||||
| #ifndef perf_misc_flags | ||||
|  |  | |||
|  | @ -508,9 +508,9 @@ void perf_trace_run_bpf_submit(void *raw_data, int size, int rctx, | |||
| static inline void | ||||
| perf_trace_buf_submit(void *raw_data, int size, int rctx, u16 type, | ||||
| 		       u64 count, struct pt_regs *regs, void *head, | ||||
| 		       struct task_struct *task) | ||||
| 		       struct task_struct *task, struct perf_event *event) | ||||
| { | ||||
| 	perf_tp_event(type, count, raw_data, size, regs, head, rctx, task); | ||||
| 	perf_tp_event(type, count, raw_data, size, regs, head, rctx, task, event); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -7906,16 +7906,15 @@ void perf_trace_run_bpf_submit(void *raw_data, int size, int rctx, | |||
| 		} | ||||
| 	} | ||||
| 	perf_tp_event(call->event.type, count, raw_data, size, regs, head, | ||||
| 		      rctx, task); | ||||
| 		      rctx, task, NULL); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(perf_trace_run_bpf_submit); | ||||
| 
 | ||||
| void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size, | ||||
| 		   struct pt_regs *regs, struct hlist_head *head, int rctx, | ||||
| 		   struct task_struct *task) | ||||
| 		   struct task_struct *task, struct perf_event *event) | ||||
| { | ||||
| 	struct perf_sample_data data; | ||||
| 	struct perf_event *event; | ||||
| 
 | ||||
| 	struct perf_raw_record raw = { | ||||
| 		.frag = { | ||||
|  | @ -7929,9 +7928,15 @@ void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size, | |||
| 
 | ||||
| 	perf_trace_buf_update(record, event_type); | ||||
| 
 | ||||
| 	hlist_for_each_entry_rcu(event, head, hlist_entry) { | ||||
| 	/* Use the given event instead of the hlist */ | ||||
| 	if (event) { | ||||
| 		if (perf_tp_event_match(event, &data, regs)) | ||||
| 			perf_swevent_event(event, count, &data, regs); | ||||
| 	} else { | ||||
| 		hlist_for_each_entry_rcu(event, head, hlist_entry) { | ||||
| 			if (perf_tp_event_match(event, &data, regs)) | ||||
| 				perf_swevent_event(event, count, &data, regs); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
|  |  | |||
|  | @ -306,6 +306,7 @@ static void | |||
| perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, | ||||
| 			  struct ftrace_ops *ops, struct pt_regs *pt_regs) | ||||
| { | ||||
| 	struct perf_event *event; | ||||
| 	struct ftrace_entry *entry; | ||||
| 	struct hlist_head *head; | ||||
| 	struct pt_regs regs; | ||||
|  | @ -329,8 +330,9 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, | |||
| 
 | ||||
| 	entry->ip = ip; | ||||
| 	entry->parent_ip = parent_ip; | ||||
| 	event = container_of(ops, struct perf_event, ftrace_ops); | ||||
| 	perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, TRACE_FN, | ||||
| 			      1, ®s, head, NULL); | ||||
| 			      1, ®s, head, NULL, event); | ||||
| 
 | ||||
| #undef ENTRY_SIZE | ||||
| } | ||||
|  |  | |||
|  | @ -1200,7 +1200,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs) | |||
| 	memset(&entry[1], 0, dsize); | ||||
| 	store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); | ||||
| 	perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, | ||||
| 			      head, NULL); | ||||
| 			      head, NULL, NULL); | ||||
| } | ||||
| NOKPROBE_SYMBOL(kprobe_perf_func); | ||||
| 
 | ||||
|  | @ -1236,7 +1236,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, | |||
| 	entry->ret_ip = (unsigned long)ri->ret_addr; | ||||
| 	store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); | ||||
| 	perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, | ||||
| 			      head, NULL); | ||||
| 			      head, NULL, NULL); | ||||
| } | ||||
| NOKPROBE_SYMBOL(kretprobe_perf_func); | ||||
| #endif	/* CONFIG_PERF_EVENTS */ | ||||
|  |  | |||
|  | @ -596,7 +596,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) | |||
| 			       (unsigned long *)&rec->args); | ||||
| 	perf_trace_buf_submit(rec, size, rctx, | ||||
| 			      sys_data->enter_event->event.type, 1, regs, | ||||
| 			      head, NULL); | ||||
| 			      head, NULL, NULL); | ||||
| } | ||||
| 
 | ||||
| static int perf_sysenter_enable(struct trace_event_call *call) | ||||
|  | @ -667,7 +667,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) | |||
| 	rec->nr = syscall_nr; | ||||
| 	rec->ret = syscall_get_return_value(current, regs); | ||||
| 	perf_trace_buf_submit(rec, size, rctx, sys_data->exit_event->event.type, | ||||
| 			      1, regs, head, NULL); | ||||
| 			      1, regs, head, NULL, NULL); | ||||
| } | ||||
| 
 | ||||
| static int perf_sysexit_enable(struct trace_event_call *call) | ||||
|  |  | |||
|  | @ -1156,7 +1156,7 @@ static void __uprobe_perf_func(struct trace_uprobe *tu, | |||
| 	} | ||||
| 
 | ||||
| 	perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, | ||||
| 			      head, NULL); | ||||
| 			      head, NULL, NULL); | ||||
|  out: | ||||
| 	preempt_enable(); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Zhou Chengming
						Zhou Chengming