aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Zanussi <tzanussi@gmail.com>2010-04-02 00:59:15 -0400
committerIngo Molnar <mingo@elte.hu>2010-04-14 05:56:05 -0400
commit8dc58101f2c838355d44402aa77646649d10dbec (patch)
treed4cc08cccfec56d37dee4c4b2383ffe56d176494
parentc05556421742eb47f80301767653a4bcb19de9de (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.c2
-rw-r--r--tools/perf/util/event.h4
-rw-r--r--tools/perf/util/header.c78
-rw-r--r--tools/perf/util/header.h8
-rw-r--r--tools/perf/util/session.c135
-rw-r--r--tools/perf/util/session.h4
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
86enum perf_header_event_type { /* above any possible kernel type */
87 PERF_RECORD_HEADER_MAX = 64,
88};
89
86typedef union event_union { 90typedef 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
430int 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
430int perf_header__write(struct perf_header *self, int fd, bool at_exit) 449int 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
521static 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
536static int perf_header__getbuffer64(struct perf_header *self, 540static 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
665int perf_header__read(struct perf_header *self, int fd) 669static 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
688static 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
703int 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
42struct perf_pipe_file_header {
43 u64 magic;
44 u64 size;
45};
46
42struct perf_header; 47struct perf_header;
43 48
44int perf_file_header__read(struct perf_file_header *self, 49int perf_file_header__read(struct perf_file_header *self,
@@ -60,8 +65,9 @@ struct perf_header {
60int perf_header__init(struct perf_header *self); 65int perf_header__init(struct perf_header *self);
61void perf_header__exit(struct perf_header *self); 66void perf_header__exit(struct perf_header *self);
62 67
63int perf_header__read(struct perf_header *self, int fd); 68int perf_header__read(struct perf_session *session, int fd);
64int perf_header__write(struct perf_header *self, int fd, bool at_exit); 69int perf_header__write(struct perf_header *self, int fd, bool at_exit);
70int perf_header__write_pipe(int fd);
65 71
66int perf_header__add_attr(struct perf_header *self, 72int 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
65void perf_session__update_sample_type(struct perf_session *self)
66{
67 self->sample_type = perf_header__sample_type(&self->header);
68}
69
55struct perf_session *perf_session__new(const char *filename, int mode, bool force) 70struct 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);
89out: 104out:
90 return self; 105 return self;
91out_free: 106out_free:
@@ -200,14 +215,17 @@ static const char *event__name[] = {
200 [PERF_RECORD_SAMPLE] = "SAMPLE", 215 [PERF_RECORD_SAMPLE] = "SAMPLE",
201}; 216};
202 217
203unsigned long event__total[PERF_RECORD_MAX]; 218unsigned long event__total[PERF_RECORD_HEADER_MAX];
204 219
205void event__print_totals(void) 220void 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
213void mem_bswap_64(void *src, int byte_size) 231void 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
277static int perf_session__process_event(struct perf_session *self, 295static 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
397int 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))
415volatile int session_done;
416
417static 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;
430more:
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;
486done:
487 err = 0;
488out_err:
489 return err;
490}
491
379int __perf_session__process_events(struct perf_session *self, 492int __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);
505out_err: 622out_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
96int do_read(int fd, void *buf, size_t size);
97void perf_session__update_sample_type(struct perf_session *self);
98
95#ifdef NO_NEWT_SUPPORT 99#ifdef NO_NEWT_SUPPORT
96static inline int perf_session__browse_hists(struct rb_root *hists __used, 100static inline int perf_session__browse_hists(struct rb_root *hists __used,
97 u64 nr_hists __used, 101 u64 nr_hists __used,