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 | |
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>
-rw-r--r-- | tools/perf/builtin-record.c | 2 | ||||
-rw-r--r-- | tools/perf/util/event.h | 4 | ||||
-rw-r--r-- | tools/perf/util/header.c | 78 | ||||
-rw-r--r-- | tools/perf/util/header.h | 8 | ||||
-rw-r--r-- | tools/perf/util/session.c | 135 | ||||
-rw-r--r-- | tools/perf/util/session.h | 4 |
6 files changed, 202 insertions, 29 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 9a9513687235..d060fc50c8a4 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -487,7 +487,7 @@ static int __cmd_record(int argc, const char **argv) | |||
487 | } | 487 | } |
488 | 488 | ||
489 | if (!file_new) { | 489 | if (!file_new) { |
490 | err = perf_header__read(&session->header, output); | 490 | err = perf_header__read(session, output); |
491 | if (err < 0) | 491 | if (err < 0) |
492 | return err; | 492 | return err; |
493 | } | 493 | } |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 7f7cf8539cfe..5c1eba671305 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -83,6 +83,10 @@ struct build_id_event { | |||
83 | char filename[]; | 83 | char filename[]; |
84 | }; | 84 | }; |
85 | 85 | ||
86 | enum perf_header_event_type { /* above any possible kernel type */ | ||
87 | PERF_RECORD_HEADER_MAX = 64, | ||
88 | }; | ||
89 | |||
86 | typedef union event_union { | 90 | typedef union event_union { |
87 | struct perf_event_header header; | 91 | struct perf_event_header header; |
88 | struct ip_event ip; | 92 | struct ip_event ip; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 6c9aa16ee51f..8d05337d1a59 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -427,6 +427,25 @@ out_free: | |||
427 | return err; | 427 | return err; |
428 | } | 428 | } |
429 | 429 | ||
430 | int perf_header__write_pipe(int fd) | ||
431 | { | ||
432 | struct perf_pipe_file_header f_header; | ||
433 | int err; | ||
434 | |||
435 | f_header = (struct perf_pipe_file_header){ | ||
436 | .magic = PERF_MAGIC, | ||
437 | .size = sizeof(f_header), | ||
438 | }; | ||
439 | |||
440 | err = do_write(fd, &f_header, sizeof(f_header)); | ||
441 | if (err < 0) { | ||
442 | pr_debug("failed to write perf pipe header\n"); | ||
443 | return err; | ||
444 | } | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
430 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) | 449 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) |
431 | { | 450 | { |
432 | struct perf_file_header f_header; | 451 | struct perf_file_header f_header; |
@@ -518,25 +537,10 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
518 | return 0; | 537 | return 0; |
519 | } | 538 | } |
520 | 539 | ||
521 | static int do_read(int fd, void *buf, size_t size) | ||
522 | { | ||
523 | while (size) { | ||
524 | int ret = read(fd, buf, size); | ||
525 | |||
526 | if (ret <= 0) | ||
527 | return -1; | ||
528 | |||
529 | size -= ret; | ||
530 | buf += ret; | ||
531 | } | ||
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static int perf_header__getbuffer64(struct perf_header *self, | 540 | static int perf_header__getbuffer64(struct perf_header *self, |
537 | int fd, void *buf, size_t size) | 541 | int fd, void *buf, size_t size) |
538 | { | 542 | { |
539 | if (do_read(fd, buf, size)) | 543 | if (do_read(fd, buf, size) <= 0) |
540 | return -1; | 544 | return -1; |
541 | 545 | ||
542 | if (self->needs_swap) | 546 | if (self->needs_swap) |
@@ -592,7 +596,7 @@ int perf_file_header__read(struct perf_file_header *self, | |||
592 | { | 596 | { |
593 | lseek(fd, 0, SEEK_SET); | 597 | lseek(fd, 0, SEEK_SET); |
594 | 598 | ||
595 | if (do_read(fd, self, sizeof(*self)) || | 599 | if (do_read(fd, self, sizeof(*self)) <= 0 || |
596 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) | 600 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) |
597 | return -1; | 601 | return -1; |
598 | 602 | ||
@@ -662,13 +666,51 @@ static int perf_file_section__process(struct perf_file_section *self, | |||
662 | return 0; | 666 | return 0; |
663 | } | 667 | } |
664 | 668 | ||
665 | int perf_header__read(struct perf_header *self, int fd) | 669 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, |
670 | struct perf_header *ph, int fd) | ||
666 | { | 671 | { |
672 | if (do_read(fd, self, sizeof(*self)) <= 0 || | ||
673 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) | ||
674 | return -1; | ||
675 | |||
676 | if (self->size != sizeof(*self)) { | ||
677 | u64 size = bswap_64(self->size); | ||
678 | |||
679 | if (size != sizeof(*self)) | ||
680 | return -1; | ||
681 | |||
682 | ph->needs_swap = true; | ||
683 | } | ||
684 | |||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | static int perf_header__read_pipe(struct perf_session *session, int fd) | ||
689 | { | ||
690 | struct perf_header *self = &session->header; | ||
691 | struct perf_pipe_file_header f_header; | ||
692 | |||
693 | if (perf_file_header__read_pipe(&f_header, self, fd) < 0) { | ||
694 | pr_debug("incompatible file format\n"); | ||
695 | return -EINVAL; | ||
696 | } | ||
697 | |||
698 | session->fd = fd; | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | int perf_header__read(struct perf_session *session, int fd) | ||
704 | { | ||
705 | struct perf_header *self = &session->header; | ||
667 | struct perf_file_header f_header; | 706 | struct perf_file_header f_header; |
668 | struct perf_file_attr f_attr; | 707 | struct perf_file_attr f_attr; |
669 | u64 f_id; | 708 | u64 f_id; |
670 | int nr_attrs, nr_ids, i, j; | 709 | int nr_attrs, nr_ids, i, j; |
671 | 710 | ||
711 | if (session->fd_pipe) | ||
712 | return perf_header__read_pipe(session, fd); | ||
713 | |||
672 | if (perf_file_header__read(&f_header, self, fd) < 0) { | 714 | if (perf_file_header__read(&f_header, self, fd) < 0) { |
673 | pr_debug("incompatible file format\n"); | 715 | pr_debug("incompatible file format\n"); |
674 | return -EINVAL; | 716 | return -EINVAL; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index c059f08cf877..6562ece67064 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -39,6 +39,11 @@ struct perf_file_header { | |||
39 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | 39 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); |
40 | }; | 40 | }; |
41 | 41 | ||
42 | struct perf_pipe_file_header { | ||
43 | u64 magic; | ||
44 | u64 size; | ||
45 | }; | ||
46 | |||
42 | struct perf_header; | 47 | struct perf_header; |
43 | 48 | ||
44 | int perf_file_header__read(struct perf_file_header *self, | 49 | int perf_file_header__read(struct perf_file_header *self, |
@@ -60,8 +65,9 @@ struct perf_header { | |||
60 | int perf_header__init(struct perf_header *self); | 65 | int perf_header__init(struct perf_header *self); |
61 | void perf_header__exit(struct perf_header *self); | 66 | void perf_header__exit(struct perf_header *self); |
62 | 67 | ||
63 | int perf_header__read(struct perf_header *self, int fd); | 68 | int perf_header__read(struct perf_session *session, int fd); |
64 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); | 69 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); |
70 | int perf_header__write_pipe(int fd); | ||
65 | 71 | ||
66 | int perf_header__add_attr(struct perf_header *self, | 72 | int perf_header__add_attr(struct perf_header *self, |
67 | struct perf_header_attr *attr); | 73 | struct perf_header_attr *attr); |
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 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 27f4c2dc715b..5f7891136655 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -27,6 +27,7 @@ struct perf_session { | |||
27 | u64 sample_type; | 27 | u64 sample_type; |
28 | struct ref_reloc_sym ref_reloc_sym; | 28 | struct ref_reloc_sym ref_reloc_sym; |
29 | int fd; | 29 | int fd; |
30 | bool fd_pipe; | ||
30 | int cwdlen; | 31 | int cwdlen; |
31 | char *cwd; | 32 | char *cwd; |
32 | char filename[0]; | 33 | char filename[0]; |
@@ -92,6 +93,9 @@ static inline struct map * | |||
92 | return map_groups__new_module(&self->kmaps, start, filename); | 93 | return map_groups__new_module(&self->kmaps, start, filename); |
93 | } | 94 | } |
94 | 95 | ||
96 | int do_read(int fd, void *buf, size_t size); | ||
97 | void perf_session__update_sample_type(struct perf_session *self); | ||
98 | |||
95 | #ifdef NO_NEWT_SUPPORT | 99 | #ifdef NO_NEWT_SUPPORT |
96 | static inline int perf_session__browse_hists(struct rb_root *hists __used, | 100 | static inline int perf_session__browse_hists(struct rb_root *hists __used, |
97 | u64 nr_hists __used, | 101 | u64 nr_hists __used, |