diff options
Diffstat (limited to 'tools/perf')
| -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, |
