diff options
author | Andrew Vagin <avagin@openvz.org> | 2012-08-07 08:56:04 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-10-26 09:22:25 -0400 |
commit | 26a031e136f4f8dc82c64df48cca0eb3b5d3eb4f (patch) | |
tree | 539d505f3a84f64cc430f87a40205be11c3c93af /tools/perf/builtin-inject.c | |
parent | e558a5bd8b74aff4690a8c55b08a1dc91ef50d7c (diff) |
perf inject: Merge sched_stat_* and sched_switch events
You may want to know where and how long a task is sleeping. A callchain
may be found in sched_switch and a time slice in stat_iowait, so I add
handler in perf inject for merging this events.
My code saves sched_switch event for each process and when it meets
stat_iowait, it reports the sched_switch event, because this event
contains a correct callchain. By another words it replaces all
stat_iowait events on proper sched_switch events.
I use the next sequence of commands for testing:
perf record -e sched:sched_stat_sleep -e sched:sched_switch \
-e sched:sched_process_exit -g -o ~/perf.data.raw \
~/test-program
perf inject -v -s -i ~/perf.data.raw -o ~/perf.data
perf report --stdio -i ~/perf.data
100.00% foo [kernel.kallsyms] [k] __schedule
|
--- __schedule
schedule
|
|--79.75%-- schedule_hrtimeout_range_clock
| schedule_hrtimeout_range
| poll_schedule_timeout
| do_select
| core_sys_select
| sys_select
| system_call_fastpath
| __select
| __libc_start_main
|
--20.25%-- do_nanosleep
hrtimer_nanosleep
sys_nanosleep
system_call_fastpath
__GI___libc_nanosleep
__libc_start_main
And here is test-program.c:
#include<unistd.h>
#include<time.h>
#include<sys/select.h>
int main()
{
struct timespec ts1;
struct timeval tv1;
int i;
long s;
for (i = 0; i < 10; i++) {
ts1.tv_sec = 0;
ts1.tv_nsec = 10000000;
nanosleep(&ts1, NULL);
tv1.tv_sec = 0;
tv1.tv_usec = 40000;
select(0, NULL, NULL, NULL,&tv1);
}
return 1;
}
Signed-off-by: Andrew Vagin <avagin@openvz.org>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1344344165-369636-4-git-send-email-avagin@openvz.org
[ committer note: Made it use evsel->handler ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-inject.c')
-rw-r--r-- | tools/perf/builtin-inject.c | 142 |
1 files changed, 139 insertions, 3 deletions
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a706ed57f94e..a4a307258fa3 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -8,19 +8,32 @@ | |||
8 | #include "builtin.h" | 8 | #include "builtin.h" |
9 | 9 | ||
10 | #include "perf.h" | 10 | #include "perf.h" |
11 | #include "util/color.h" | ||
12 | #include "util/evlist.h" | ||
13 | #include "util/evsel.h" | ||
11 | #include "util/session.h" | 14 | #include "util/session.h" |
12 | #include "util/tool.h" | 15 | #include "util/tool.h" |
13 | #include "util/debug.h" | 16 | #include "util/debug.h" |
14 | 17 | ||
15 | #include "util/parse-options.h" | 18 | #include "util/parse-options.h" |
16 | 19 | ||
20 | #include <linux/list.h> | ||
21 | |||
17 | struct perf_inject { | 22 | struct perf_inject { |
18 | struct perf_tool tool; | 23 | struct perf_tool tool; |
19 | bool build_ids; | 24 | bool build_ids; |
25 | bool sched_stat; | ||
20 | const char *input_name; | 26 | const char *input_name; |
21 | int pipe_output, | 27 | int pipe_output, |
22 | output; | 28 | output; |
23 | u64 bytes_written; | 29 | u64 bytes_written; |
30 | struct list_head samples; | ||
31 | }; | ||
32 | |||
33 | struct event_entry { | ||
34 | struct list_head node; | ||
35 | u32 tid; | ||
36 | union perf_event event[0]; | ||
24 | }; | 37 | }; |
25 | 38 | ||
26 | static int perf_event__repipe_synth(struct perf_tool *tool, | 39 | static int perf_event__repipe_synth(struct perf_tool *tool, |
@@ -86,12 +99,23 @@ static int perf_event__repipe(struct perf_tool *tool, | |||
86 | return perf_event__repipe_synth(tool, event, machine); | 99 | return perf_event__repipe_synth(tool, event, machine); |
87 | } | 100 | } |
88 | 101 | ||
102 | typedef int (*inject_handler)(struct perf_tool *tool, | ||
103 | union perf_event *event, | ||
104 | struct perf_sample *sample, | ||
105 | struct perf_evsel *evsel, | ||
106 | struct machine *machine); | ||
107 | |||
89 | static int perf_event__repipe_sample(struct perf_tool *tool, | 108 | static int perf_event__repipe_sample(struct perf_tool *tool, |
90 | union perf_event *event, | 109 | union perf_event *event, |
91 | struct perf_sample *sample __maybe_unused, | 110 | struct perf_sample *sample, |
92 | struct perf_evsel *evsel __maybe_unused, | 111 | struct perf_evsel *evsel, |
93 | struct machine *machine) | 112 | struct machine *machine) |
94 | { | 113 | { |
114 | if (evsel->handler.func) { | ||
115 | inject_handler f = evsel->handler.func; | ||
116 | return f(tool, event, sample, evsel, machine); | ||
117 | } | ||
118 | |||
95 | return perf_event__repipe_synth(tool, event, machine); | 119 | return perf_event__repipe_synth(tool, event, machine); |
96 | } | 120 | } |
97 | 121 | ||
@@ -216,6 +240,79 @@ repipe: | |||
216 | return 0; | 240 | return 0; |
217 | } | 241 | } |
218 | 242 | ||
243 | static int perf_inject__sched_process_exit(struct perf_tool *tool, | ||
244 | union perf_event *event __maybe_unused, | ||
245 | struct perf_sample *sample, | ||
246 | struct perf_evsel *evsel __maybe_unused, | ||
247 | struct machine *machine __maybe_unused) | ||
248 | { | ||
249 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
250 | struct event_entry *ent; | ||
251 | |||
252 | list_for_each_entry(ent, &inject->samples, node) { | ||
253 | if (sample->tid == ent->tid) { | ||
254 | list_del_init(&ent->node); | ||
255 | free(ent); | ||
256 | break; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static int perf_inject__sched_switch(struct perf_tool *tool, | ||
264 | union perf_event *event, | ||
265 | struct perf_sample *sample, | ||
266 | struct perf_evsel *evsel, | ||
267 | struct machine *machine) | ||
268 | { | ||
269 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
270 | struct event_entry *ent; | ||
271 | |||
272 | perf_inject__sched_process_exit(tool, event, sample, evsel, machine); | ||
273 | |||
274 | ent = malloc(event->header.size + sizeof(struct event_entry)); | ||
275 | if (ent == NULL) { | ||
276 | color_fprintf(stderr, PERF_COLOR_RED, | ||
277 | "Not enough memory to process sched switch event!"); | ||
278 | return -1; | ||
279 | } | ||
280 | |||
281 | ent->tid = sample->tid; | ||
282 | memcpy(&ent->event, event, event->header.size); | ||
283 | list_add(&ent->node, &inject->samples); | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | static int perf_inject__sched_stat(struct perf_tool *tool, | ||
288 | union perf_event *event __maybe_unused, | ||
289 | struct perf_sample *sample, | ||
290 | struct perf_evsel *evsel, | ||
291 | struct machine *machine) | ||
292 | { | ||
293 | struct event_entry *ent; | ||
294 | union perf_event *event_sw; | ||
295 | struct perf_sample sample_sw; | ||
296 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
297 | u32 pid = perf_evsel__intval(evsel, sample, "pid"); | ||
298 | |||
299 | list_for_each_entry(ent, &inject->samples, node) { | ||
300 | if (pid == ent->tid) | ||
301 | goto found; | ||
302 | } | ||
303 | |||
304 | return 0; | ||
305 | found: | ||
306 | event_sw = &ent->event[0]; | ||
307 | perf_evsel__parse_sample(evsel, event_sw, &sample_sw); | ||
308 | |||
309 | sample_sw.period = sample->period; | ||
310 | sample_sw.time = sample->time; | ||
311 | perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, | ||
312 | &sample_sw, false); | ||
313 | return perf_event__repipe(tool, event_sw, &sample_sw, machine); | ||
314 | } | ||
315 | |||
219 | extern volatile int session_done; | 316 | extern volatile int session_done; |
220 | 317 | ||
221 | static void sig_handler(int sig __maybe_unused) | 318 | static void sig_handler(int sig __maybe_unused) |
@@ -223,6 +320,21 @@ static void sig_handler(int sig __maybe_unused) | |||
223 | session_done = 1; | 320 | session_done = 1; |
224 | } | 321 | } |
225 | 322 | ||
323 | static int perf_evsel__check_stype(struct perf_evsel *evsel, | ||
324 | u64 sample_type, const char *sample_msg) | ||
325 | { | ||
326 | struct perf_event_attr *attr = &evsel->attr; | ||
327 | const char *name = perf_evsel__name(evsel); | ||
328 | |||
329 | if (!(attr->sample_type & sample_type)) { | ||
330 | pr_err("Samples for %s event do not have %s attribute set.", | ||
331 | name, sample_msg); | ||
332 | return -EINVAL; | ||
333 | } | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
226 | static int __cmd_inject(struct perf_inject *inject) | 338 | static int __cmd_inject(struct perf_inject *inject) |
227 | { | 339 | { |
228 | struct perf_session *session; | 340 | struct perf_session *session; |
@@ -241,6 +353,26 @@ static int __cmd_inject(struct perf_inject *inject) | |||
241 | if (session == NULL) | 353 | if (session == NULL) |
242 | return -ENOMEM; | 354 | return -ENOMEM; |
243 | 355 | ||
356 | if (inject->sched_stat) { | ||
357 | struct perf_evsel *evsel; | ||
358 | |||
359 | inject->tool.ordered_samples = true; | ||
360 | |||
361 | list_for_each_entry(evsel, &session->evlist->entries, node) { | ||
362 | const char *name = perf_evsel__name(evsel); | ||
363 | |||
364 | if (!strcmp(name, "sched:sched_switch")) { | ||
365 | if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID")) | ||
366 | return -EINVAL; | ||
367 | |||
368 | evsel->handler.func = perf_inject__sched_switch; | ||
369 | } else if (!strcmp(name, "sched:sched_process_exit")) | ||
370 | evsel->handler.func = perf_inject__sched_process_exit; | ||
371 | else if (!strncmp(name, "sched:sched_stat_", 17)) | ||
372 | evsel->handler.func = perf_inject__sched_stat; | ||
373 | } | ||
374 | } | ||
375 | |||
244 | if (!inject->pipe_output) | 376 | if (!inject->pipe_output) |
245 | lseek(inject->output, session->header.data_offset, SEEK_SET); | 377 | lseek(inject->output, session->header.data_offset, SEEK_SET); |
246 | 378 | ||
@@ -275,6 +407,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
275 | .build_id = perf_event__repipe_op2_synth, | 407 | .build_id = perf_event__repipe_op2_synth, |
276 | }, | 408 | }, |
277 | .input_name = "-", | 409 | .input_name = "-", |
410 | .samples = LIST_HEAD_INIT(inject.samples), | ||
278 | }; | 411 | }; |
279 | const char *output_name = "-"; | 412 | const char *output_name = "-"; |
280 | const struct option options[] = { | 413 | const struct option options[] = { |
@@ -284,6 +417,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
284 | "input file name"), | 417 | "input file name"), |
285 | OPT_STRING('o', "output", &output_name, "file", | 418 | OPT_STRING('o', "output", &output_name, "file", |
286 | "output file name"), | 419 | "output file name"), |
420 | OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, | ||
421 | "Merge sched-stat and sched-switch for getting events " | ||
422 | "where and how long tasks slept"), | ||
287 | OPT_INCR('v', "verbose", &verbose, | 423 | OPT_INCR('v', "verbose", &verbose, |
288 | "be more verbose (show build ids, etc)"), | 424 | "be more verbose (show build ids, etc)"), |
289 | OPT_END() | 425 | OPT_END() |