diff options
author | Andrew Vagin <avagin@openvz.org> | 2012-07-11 10:14:58 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-07-31 11:02:05 -0400 |
commit | e6dab5ffab59e910ec0e3355f4a6f29f7a7be474 (patch) | |
tree | 87acf0fb071b8d09794ac7d834cb256de030cceb /kernel/events | |
parent | d07bdfd322d307789f15b427dbcc39257665356f (diff) |
perf/trace: Add ability to set a target task for events
A few events are interesting not only for a current task.
For example, sched_stat_* events are interesting for a task
which wakes up. For this reason, it will be good if such
events will be delivered to a target task too.
Now a target task can be set by using __perf_task().
The original idea and a draft patch belongs to Peter Zijlstra.
I need these events for profiling sleep times. sched_switch is used for
getting callchains and sched_stat_* is used for getting time periods.
These events are combined in user space, then it can be analyzed by
perf tools.
Inspired-by: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Arun Sharma <asharma@fb.com>
Signed-off-by: Andrew Vagin <avagin@openvz.org>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1342016098-213063-1-git-send-email-avagin@openvz.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/events')
-rw-r--r-- | kernel/events/callchain.c | 9 | ||||
-rw-r--r-- | kernel/events/core.c | 30 | ||||
-rw-r--r-- | kernel/events/internal.h | 3 |
3 files changed, 38 insertions, 4 deletions
diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c index 6581a040f39..98d4597f43d 100644 --- a/kernel/events/callchain.c +++ b/kernel/events/callchain.c | |||
@@ -153,7 +153,8 @@ put_callchain_entry(int rctx) | |||
153 | put_recursion_context(__get_cpu_var(callchain_recursion), rctx); | 153 | put_recursion_context(__get_cpu_var(callchain_recursion), rctx); |
154 | } | 154 | } |
155 | 155 | ||
156 | struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) | 156 | struct perf_callchain_entry * |
157 | perf_callchain(struct perf_event *event, struct pt_regs *regs) | ||
157 | { | 158 | { |
158 | int rctx; | 159 | int rctx; |
159 | struct perf_callchain_entry *entry; | 160 | struct perf_callchain_entry *entry; |
@@ -178,6 +179,12 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) | |||
178 | } | 179 | } |
179 | 180 | ||
180 | if (regs) { | 181 | if (regs) { |
182 | /* | ||
183 | * Disallow cross-task user callchains. | ||
184 | */ | ||
185 | if (event->ctx->task && event->ctx->task != current) | ||
186 | goto exit_put; | ||
187 | |||
181 | perf_callchain_store(entry, PERF_CONTEXT_USER); | 188 | perf_callchain_store(entry, PERF_CONTEXT_USER); |
182 | perf_callchain_user(entry, regs); | 189 | perf_callchain_user(entry, regs); |
183 | } | 190 | } |
diff --git a/kernel/events/core.c b/kernel/events/core.c index f1cf0edeb39..b7935fcec7d 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -4039,7 +4039,7 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
4039 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { | 4039 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { |
4040 | int size = 1; | 4040 | int size = 1; |
4041 | 4041 | ||
4042 | data->callchain = perf_callchain(regs); | 4042 | data->callchain = perf_callchain(event, regs); |
4043 | 4043 | ||
4044 | if (data->callchain) | 4044 | if (data->callchain) |
4045 | size += data->callchain->nr; | 4045 | size += data->callchain->nr; |
@@ -5209,7 +5209,8 @@ static int perf_tp_event_match(struct perf_event *event, | |||
5209 | } | 5209 | } |
5210 | 5210 | ||
5211 | void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, | 5211 | void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, |
5212 | struct pt_regs *regs, struct hlist_head *head, int rctx) | 5212 | struct pt_regs *regs, struct hlist_head *head, int rctx, |
5213 | struct task_struct *task) | ||
5213 | { | 5214 | { |
5214 | struct perf_sample_data data; | 5215 | struct perf_sample_data data; |
5215 | struct perf_event *event; | 5216 | struct perf_event *event; |
@@ -5228,6 +5229,31 @@ void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, | |||
5228 | perf_swevent_event(event, count, &data, regs); | 5229 | perf_swevent_event(event, count, &data, regs); |
5229 | } | 5230 | } |
5230 | 5231 | ||
5232 | /* | ||
5233 | * If we got specified a target task, also iterate its context and | ||
5234 | * deliver this event there too. | ||
5235 | */ | ||
5236 | if (task && task != current) { | ||
5237 | struct perf_event_context *ctx; | ||
5238 | struct trace_entry *entry = record; | ||
5239 | |||
5240 | rcu_read_lock(); | ||
5241 | ctx = rcu_dereference(task->perf_event_ctxp[perf_sw_context]); | ||
5242 | if (!ctx) | ||
5243 | goto unlock; | ||
5244 | |||
5245 | list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { | ||
5246 | if (event->attr.type != PERF_TYPE_TRACEPOINT) | ||
5247 | continue; | ||
5248 | if (event->attr.config != entry->type) | ||
5249 | continue; | ||
5250 | if (perf_tp_event_match(event, &data, regs)) | ||
5251 | perf_swevent_event(event, count, &data, regs); | ||
5252 | } | ||
5253 | unlock: | ||
5254 | rcu_read_unlock(); | ||
5255 | } | ||
5256 | |||
5231 | perf_swevent_put_recursion_context(rctx); | 5257 | perf_swevent_put_recursion_context(rctx); |
5232 | } | 5258 | } |
5233 | EXPORT_SYMBOL_GPL(perf_tp_event); | 5259 | EXPORT_SYMBOL_GPL(perf_tp_event); |
diff --git a/kernel/events/internal.h b/kernel/events/internal.h index b0b107f90af..a096c19f2c2 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h | |||
@@ -101,7 +101,8 @@ __output_copy(struct perf_output_handle *handle, | |||
101 | } | 101 | } |
102 | 102 | ||
103 | /* Callchain handling */ | 103 | /* Callchain handling */ |
104 | extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs); | 104 | extern struct perf_callchain_entry * |
105 | perf_callchain(struct perf_event *event, struct pt_regs *regs); | ||
105 | extern int get_callchain_buffers(void); | 106 | extern int get_callchain_buffers(void); |
106 | extern void put_callchain_buffers(void); | 107 | extern void put_callchain_buffers(void); |
107 | 108 | ||