diff options
author | Tom Zanussi <tzanussi@gmail.com> | 2010-04-02 00:59:15 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-04-14 05:56:05 -0400 |
commit | 8dc58101f2c838355d44402aa77646649d10dbec (patch) | |
tree | d4cc08cccfec56d37dee4c4b2383ffe56d176494 /tools/perf/util/session.c | |
parent | c05556421742eb47f80301767653a4bcb19de9de (diff) |
perf: Add pipe-specific header read/write and event processing code
This patch makes several changes to allow the perf event stream
to be sent and received over a pipe:
- adds pipe-specific versions of the header read/write code
- adds pipe-specific version of the event processing code
- adds a range of event types to be used for header or other
pseudo events, above the range used by the kernel
- checks the return value of event handlers, which they can use
to skip over large events during event processing rather than actually
reading them into event objects.
- unifies the multiple do_read() functions and updates its
users.
Note that none of these changes affect the existing perf data
file format or processing - this code only comes into play if
perf output is sent to stdout (or is read from stdin).
Signed-off-by: Tom Zanussi <tzanussi@gmail.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Cc: fweisbec@gmail.com
Cc: rostedt@goodmis.org
Cc: k-keiichi@bx.jp.nec.com
Cc: acme@ghostprotocols.net
LKML-Reference: <1270184365-8281-2-git-send-email-tzanussi@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util/session.c')
-rw-r--r-- | tools/perf/util/session.c | 135 |
1 files changed, 126 insertions, 9 deletions
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ddf288fca3eb..2c1277cb4ae4 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -14,6 +14,16 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
14 | { | 14 | { |
15 | struct stat input_stat; | 15 | struct stat input_stat; |
16 | 16 | ||
17 | if (!strcmp(self->filename, "-")) { | ||
18 | self->fd_pipe = true; | ||
19 | self->fd = STDIN_FILENO; | ||
20 | |||
21 | if (perf_header__read(self, self->fd) < 0) | ||
22 | pr_err("incompatible file format"); | ||
23 | |||
24 | return 0; | ||
25 | } | ||
26 | |||
17 | self->fd = open(self->filename, O_RDONLY); | 27 | self->fd = open(self->filename, O_RDONLY); |
18 | if (self->fd < 0) { | 28 | if (self->fd < 0) { |
19 | pr_err("failed to open file: %s", self->filename); | 29 | pr_err("failed to open file: %s", self->filename); |
@@ -38,7 +48,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
38 | goto out_close; | 48 | goto out_close; |
39 | } | 49 | } |
40 | 50 | ||
41 | if (perf_header__read(&self->header, self->fd) < 0) { | 51 | if (perf_header__read(self, self->fd) < 0) { |
42 | pr_err("incompatible file format"); | 52 | pr_err("incompatible file format"); |
43 | goto out_close; | 53 | goto out_close; |
44 | } | 54 | } |
@@ -52,6 +62,11 @@ out_close: | |||
52 | return -1; | 62 | return -1; |
53 | } | 63 | } |
54 | 64 | ||
65 | void perf_session__update_sample_type(struct perf_session *self) | ||
66 | { | ||
67 | self->sample_type = perf_header__sample_type(&self->header); | ||
68 | } | ||
69 | |||
55 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | 70 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) |
56 | { | 71 | { |
57 | size_t len = filename ? strlen(filename) + 1 : 0; | 72 | size_t len = filename ? strlen(filename) + 1 : 0; |
@@ -85,7 +100,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
85 | goto out_delete; | 100 | goto out_delete; |
86 | } | 101 | } |
87 | 102 | ||
88 | self->sample_type = perf_header__sample_type(&self->header); | 103 | perf_session__update_sample_type(self); |
89 | out: | 104 | out: |
90 | return self; | 105 | return self; |
91 | out_free: | 106 | out_free: |
@@ -200,14 +215,17 @@ static const char *event__name[] = { | |||
200 | [PERF_RECORD_SAMPLE] = "SAMPLE", | 215 | [PERF_RECORD_SAMPLE] = "SAMPLE", |
201 | }; | 216 | }; |
202 | 217 | ||
203 | unsigned long event__total[PERF_RECORD_MAX]; | 218 | unsigned long event__total[PERF_RECORD_HEADER_MAX]; |
204 | 219 | ||
205 | void event__print_totals(void) | 220 | void event__print_totals(void) |
206 | { | 221 | { |
207 | int i; | 222 | int i; |
208 | for (i = 0; i < PERF_RECORD_MAX; ++i) | 223 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
224 | if (!event__name[i]) | ||
225 | continue; | ||
209 | pr_info("%10s events: %10ld\n", | 226 | pr_info("%10s events: %10ld\n", |
210 | event__name[i], event__total[i]); | 227 | event__name[i], event__total[i]); |
228 | } | ||
211 | } | 229 | } |
212 | 230 | ||
213 | void mem_bswap_64(void *src, int byte_size) | 231 | void mem_bswap_64(void *src, int byte_size) |
@@ -271,7 +289,7 @@ static event__swap_op event__swap_ops[] = { | |||
271 | [PERF_RECORD_LOST] = event__all64_swap, | 289 | [PERF_RECORD_LOST] = event__all64_swap, |
272 | [PERF_RECORD_READ] = event__read_swap, | 290 | [PERF_RECORD_READ] = event__read_swap, |
273 | [PERF_RECORD_SAMPLE] = event__all64_swap, | 291 | [PERF_RECORD_SAMPLE] = event__all64_swap, |
274 | [PERF_RECORD_MAX] = NULL, | 292 | [PERF_RECORD_HEADER_MAX] = NULL, |
275 | }; | 293 | }; |
276 | 294 | ||
277 | static int perf_session__process_event(struct perf_session *self, | 295 | static int perf_session__process_event(struct perf_session *self, |
@@ -281,7 +299,7 @@ static int perf_session__process_event(struct perf_session *self, | |||
281 | { | 299 | { |
282 | trace_event(event); | 300 | trace_event(event); |
283 | 301 | ||
284 | if (event->header.type < PERF_RECORD_MAX) { | 302 | if (event->header.type < PERF_RECORD_HEADER_MAX) { |
285 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | 303 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", |
286 | offset + head, event->header.size, | 304 | offset + head, event->header.size, |
287 | event__name[event->header.type]); | 305 | event__name[event->header.type]); |
@@ -376,6 +394,101 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se | |||
376 | return thread; | 394 | return thread; |
377 | } | 395 | } |
378 | 396 | ||
397 | int do_read(int fd, void *buf, size_t size) | ||
398 | { | ||
399 | void *buf_start = buf; | ||
400 | |||
401 | while (size) { | ||
402 | int ret = read(fd, buf, size); | ||
403 | |||
404 | if (ret <= 0) | ||
405 | return ret; | ||
406 | |||
407 | size -= ret; | ||
408 | buf += ret; | ||
409 | } | ||
410 | |||
411 | return buf - buf_start; | ||
412 | } | ||
413 | |||
414 | #define session_done() (*(volatile int *)(&session_done)) | ||
415 | volatile int session_done; | ||
416 | |||
417 | static int __perf_session__process_pipe_events(struct perf_session *self, | ||
418 | struct perf_event_ops *ops) | ||
419 | { | ||
420 | event_t event; | ||
421 | uint32_t size; | ||
422 | int skip = 0; | ||
423 | u64 head; | ||
424 | int err; | ||
425 | void *p; | ||
426 | |||
427 | perf_event_ops__fill_defaults(ops); | ||
428 | |||
429 | head = 0; | ||
430 | more: | ||
431 | err = do_read(self->fd, &event, sizeof(struct perf_event_header)); | ||
432 | if (err <= 0) { | ||
433 | if (err == 0) | ||
434 | goto done; | ||
435 | |||
436 | pr_err("failed to read event header\n"); | ||
437 | goto out_err; | ||
438 | } | ||
439 | |||
440 | if (self->header.needs_swap) | ||
441 | perf_event_header__bswap(&event.header); | ||
442 | |||
443 | size = event.header.size; | ||
444 | if (size == 0) | ||
445 | size = 8; | ||
446 | |||
447 | p = &event; | ||
448 | p += sizeof(struct perf_event_header); | ||
449 | |||
450 | err = do_read(self->fd, p, size - sizeof(struct perf_event_header)); | ||
451 | if (err <= 0) { | ||
452 | if (err == 0) { | ||
453 | pr_err("unexpected end of event stream\n"); | ||
454 | goto done; | ||
455 | } | ||
456 | |||
457 | pr_err("failed to read event data\n"); | ||
458 | goto out_err; | ||
459 | } | ||
460 | |||
461 | if (size == 0 || | ||
462 | (skip = perf_session__process_event(self, &event, ops, | ||
463 | 0, head)) < 0) { | ||
464 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | ||
465 | head, event.header.size, event.header.type); | ||
466 | /* | ||
467 | * assume we lost track of the stream, check alignment, and | ||
468 | * increment a single u64 in the hope to catch on again 'soon'. | ||
469 | */ | ||
470 | if (unlikely(head & 7)) | ||
471 | head &= ~7ULL; | ||
472 | |||
473 | size = 8; | ||
474 | } | ||
475 | |||
476 | head += size; | ||
477 | |||
478 | dump_printf("\n%#Lx [%#x]: event: %d\n", | ||
479 | head, event.header.size, event.header.type); | ||
480 | |||
481 | if (skip > 0) | ||
482 | head += skip; | ||
483 | |||
484 | if (!session_done()) | ||
485 | goto more; | ||
486 | done: | ||
487 | err = 0; | ||
488 | out_err: | ||
489 | return err; | ||
490 | } | ||
491 | |||
379 | int __perf_session__process_events(struct perf_session *self, | 492 | int __perf_session__process_events(struct perf_session *self, |
380 | u64 data_offset, u64 data_size, | 493 | u64 data_offset, u64 data_size, |
381 | u64 file_size, struct perf_event_ops *ops) | 494 | u64 file_size, struct perf_event_ops *ops) |
@@ -499,9 +612,13 @@ out_getcwd_err: | |||
499 | self->cwdlen = strlen(self->cwd); | 612 | self->cwdlen = strlen(self->cwd); |
500 | } | 613 | } |
501 | 614 | ||
502 | err = __perf_session__process_events(self, self->header.data_offset, | 615 | if (!self->fd_pipe) |
503 | self->header.data_size, | 616 | err = __perf_session__process_events(self, |
504 | self->size, ops); | 617 | self->header.data_offset, |
618 | self->header.data_size, | ||
619 | self->size, ops); | ||
620 | else | ||
621 | err = __perf_session__process_pipe_events(self, ops); | ||
505 | out_err: | 622 | out_err: |
506 | return err; | 623 | return err; |
507 | } | 624 | } |