diff options
Diffstat (limited to 'tools/perf/util/session.c')
-rw-r--r-- | tools/perf/util/session.c | 226 |
1 files changed, 177 insertions, 49 deletions
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 52672dad1fe9..3074d38897e6 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -65,9 +65,49 @@ out_close: | |||
65 | return -1; | 65 | return -1; |
66 | } | 66 | } |
67 | 67 | ||
68 | static void perf_session__id_header_size(struct perf_session *session) | ||
69 | { | ||
70 | struct sample_data *data; | ||
71 | u64 sample_type = session->sample_type; | ||
72 | u16 size = 0; | ||
73 | |||
74 | if (!session->sample_id_all) | ||
75 | goto out; | ||
76 | |||
77 | if (sample_type & PERF_SAMPLE_TID) | ||
78 | size += sizeof(data->tid) * 2; | ||
79 | |||
80 | if (sample_type & PERF_SAMPLE_TIME) | ||
81 | size += sizeof(data->time); | ||
82 | |||
83 | if (sample_type & PERF_SAMPLE_ID) | ||
84 | size += sizeof(data->id); | ||
85 | |||
86 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
87 | size += sizeof(data->stream_id); | ||
88 | |||
89 | if (sample_type & PERF_SAMPLE_CPU) | ||
90 | size += sizeof(data->cpu) * 2; | ||
91 | out: | ||
92 | session->id_hdr_size = size; | ||
93 | } | ||
94 | |||
95 | void perf_session__set_sample_id_all(struct perf_session *session, bool value) | ||
96 | { | ||
97 | session->sample_id_all = value; | ||
98 | perf_session__id_header_size(session); | ||
99 | } | ||
100 | |||
101 | void perf_session__set_sample_type(struct perf_session *session, u64 type) | ||
102 | { | ||
103 | session->sample_type = type; | ||
104 | } | ||
105 | |||
68 | void perf_session__update_sample_type(struct perf_session *self) | 106 | void perf_session__update_sample_type(struct perf_session *self) |
69 | { | 107 | { |
70 | self->sample_type = perf_header__sample_type(&self->header); | 108 | self->sample_type = perf_header__sample_type(&self->header); |
109 | self->sample_id_all = perf_header__sample_id_all(&self->header); | ||
110 | perf_session__id_header_size(self); | ||
71 | } | 111 | } |
72 | 112 | ||
73 | int perf_session__create_kernel_maps(struct perf_session *self) | 113 | int perf_session__create_kernel_maps(struct perf_session *self) |
@@ -240,7 +280,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | |||
240 | return syms; | 280 | return syms; |
241 | } | 281 | } |
242 | 282 | ||
283 | static int process_event_synth_stub(event_t *event __used, | ||
284 | struct perf_session *session __used) | ||
285 | { | ||
286 | dump_printf(": unhandled!\n"); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
243 | static int process_event_stub(event_t *event __used, | 290 | static int process_event_stub(event_t *event __used, |
291 | struct sample_data *sample __used, | ||
244 | struct perf_session *session __used) | 292 | struct perf_session *session __used) |
245 | { | 293 | { |
246 | dump_printf(": unhandled!\n"); | 294 | dump_printf(": unhandled!\n"); |
@@ -280,13 +328,13 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | |||
280 | if (handler->unthrottle == NULL) | 328 | if (handler->unthrottle == NULL) |
281 | handler->unthrottle = process_event_stub; | 329 | handler->unthrottle = process_event_stub; |
282 | if (handler->attr == NULL) | 330 | if (handler->attr == NULL) |
283 | handler->attr = process_event_stub; | 331 | handler->attr = process_event_synth_stub; |
284 | if (handler->event_type == NULL) | 332 | if (handler->event_type == NULL) |
285 | handler->event_type = process_event_stub; | 333 | handler->event_type = process_event_synth_stub; |
286 | if (handler->tracing_data == NULL) | 334 | if (handler->tracing_data == NULL) |
287 | handler->tracing_data = process_event_stub; | 335 | handler->tracing_data = process_event_synth_stub; |
288 | if (handler->build_id == NULL) | 336 | if (handler->build_id == NULL) |
289 | handler->build_id = process_event_stub; | 337 | handler->build_id = process_event_synth_stub; |
290 | if (handler->finished_round == NULL) { | 338 | if (handler->finished_round == NULL) { |
291 | if (handler->ordered_samples) | 339 | if (handler->ordered_samples) |
292 | handler->finished_round = process_finished_round; | 340 | handler->finished_round = process_finished_round; |
@@ -413,12 +461,18 @@ static void perf_session_free_sample_buffers(struct perf_session *session) | |||
413 | } | 461 | } |
414 | } | 462 | } |
415 | 463 | ||
464 | static int perf_session_deliver_event(struct perf_session *session, | ||
465 | event_t *event, | ||
466 | struct sample_data *sample, | ||
467 | struct perf_event_ops *ops); | ||
468 | |||
416 | static void flush_sample_queue(struct perf_session *s, | 469 | static void flush_sample_queue(struct perf_session *s, |
417 | struct perf_event_ops *ops) | 470 | struct perf_event_ops *ops) |
418 | { | 471 | { |
419 | struct ordered_samples *os = &s->ordered_samples; | 472 | struct ordered_samples *os = &s->ordered_samples; |
420 | struct list_head *head = &os->samples; | 473 | struct list_head *head = &os->samples; |
421 | struct sample_queue *tmp, *iter; | 474 | struct sample_queue *tmp, *iter; |
475 | struct sample_data sample; | ||
422 | u64 limit = os->next_flush; | 476 | u64 limit = os->next_flush; |
423 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | 477 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; |
424 | 478 | ||
@@ -429,7 +483,8 @@ static void flush_sample_queue(struct perf_session *s, | |||
429 | if (iter->timestamp > limit) | 483 | if (iter->timestamp > limit) |
430 | break; | 484 | break; |
431 | 485 | ||
432 | ops->sample(iter->event, s); | 486 | event__parse_sample(iter->event, s, &sample); |
487 | perf_session_deliver_event(s, iter->event, &sample, ops); | ||
433 | 488 | ||
434 | os->last_flush = iter->timestamp; | 489 | os->last_flush = iter->timestamp; |
435 | list_del(&iter->list); | 490 | list_del(&iter->list); |
@@ -494,8 +549,7 @@ static int process_finished_round(event_t *event __used, | |||
494 | } | 549 | } |
495 | 550 | ||
496 | /* The queue is ordered by time */ | 551 | /* The queue is ordered by time */ |
497 | static void __queue_sample_event(struct sample_queue *new, | 552 | static void __queue_event(struct sample_queue *new, struct perf_session *s) |
498 | struct perf_session *s) | ||
499 | { | 553 | { |
500 | struct ordered_samples *os = &s->ordered_samples; | 554 | struct ordered_samples *os = &s->ordered_samples; |
501 | struct sample_queue *sample = os->last_sample; | 555 | struct sample_queue *sample = os->last_sample; |
@@ -541,14 +595,17 @@ static void __queue_sample_event(struct sample_queue *new, | |||
541 | 595 | ||
542 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) | 596 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) |
543 | 597 | ||
544 | static int queue_sample_event(event_t *event, struct sample_data *data, | 598 | static int perf_session_queue_event(struct perf_session *s, event_t *event, |
545 | struct perf_session *s) | 599 | struct sample_data *data) |
546 | { | 600 | { |
547 | struct ordered_samples *os = &s->ordered_samples; | 601 | struct ordered_samples *os = &s->ordered_samples; |
548 | struct list_head *sc = &os->sample_cache; | 602 | struct list_head *sc = &os->sample_cache; |
549 | u64 timestamp = data->time; | 603 | u64 timestamp = data->time; |
550 | struct sample_queue *new; | 604 | struct sample_queue *new; |
551 | 605 | ||
606 | if (!timestamp) | ||
607 | return -ETIME; | ||
608 | |||
552 | if (timestamp < s->ordered_samples.last_flush) { | 609 | if (timestamp < s->ordered_samples.last_flush) { |
553 | printf("Warning: Timestamp below last timeslice flush\n"); | 610 | printf("Warning: Timestamp below last timeslice flush\n"); |
554 | return -EINVAL; | 611 | return -EINVAL; |
@@ -573,79 +630,142 @@ static int queue_sample_event(event_t *event, struct sample_data *data, | |||
573 | new->timestamp = timestamp; | 630 | new->timestamp = timestamp; |
574 | new->event = event; | 631 | new->event = event; |
575 | 632 | ||
576 | __queue_sample_event(new, s); | 633 | __queue_event(new, s); |
577 | 634 | ||
578 | return 0; | 635 | return 0; |
579 | } | 636 | } |
580 | 637 | ||
581 | static int perf_session__process_sample(event_t *event, struct perf_session *s, | 638 | static void callchain__dump(struct sample_data *sample) |
582 | struct perf_event_ops *ops) | ||
583 | { | 639 | { |
584 | struct sample_data data; | 640 | unsigned int i; |
585 | |||
586 | if (!ops->ordered_samples) | ||
587 | return ops->sample(event, s); | ||
588 | 641 | ||
589 | bzero(&data, sizeof(struct sample_data)); | 642 | if (!dump_trace) |
590 | event__parse_sample(event, s->sample_type, &data); | 643 | return; |
591 | 644 | ||
592 | queue_sample_event(event, &data, s); | 645 | printf("... chain: nr:%Lu\n", sample->callchain->nr); |
593 | 646 | ||
594 | return 0; | 647 | for (i = 0; i < sample->callchain->nr; i++) |
648 | printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]); | ||
595 | } | 649 | } |
596 | 650 | ||
597 | static int perf_session__process_event(struct perf_session *self, | 651 | static void perf_session__print_tstamp(struct perf_session *session, |
598 | event_t *event, | 652 | event_t *event, |
599 | struct perf_event_ops *ops, | 653 | struct sample_data *sample) |
600 | u64 file_offset) | ||
601 | { | 654 | { |
602 | trace_event(event); | 655 | if (event->header.type != PERF_RECORD_SAMPLE && |
603 | 656 | !session->sample_id_all) { | |
604 | if (event->header.type < PERF_RECORD_HEADER_MAX) { | 657 | fputs("-1 -1 ", stdout); |
605 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | 658 | return; |
606 | file_offset, event->header.size, | ||
607 | event__name[event->header.type]); | ||
608 | hists__inc_nr_events(&self->hists, event->header.type); | ||
609 | } | 659 | } |
610 | 660 | ||
611 | if (self->header.needs_swap && event__swap_ops[event->header.type]) | 661 | if ((session->sample_type & PERF_SAMPLE_CPU)) |
612 | event__swap_ops[event->header.type](event); | 662 | printf("%u ", sample->cpu); |
613 | 663 | ||
664 | if (session->sample_type & PERF_SAMPLE_TIME) | ||
665 | printf("%Lu ", sample->time); | ||
666 | } | ||
667 | |||
668 | static int perf_session_deliver_event(struct perf_session *session, | ||
669 | event_t *event, | ||
670 | struct sample_data *sample, | ||
671 | struct perf_event_ops *ops) | ||
672 | { | ||
614 | switch (event->header.type) { | 673 | switch (event->header.type) { |
615 | case PERF_RECORD_SAMPLE: | 674 | case PERF_RECORD_SAMPLE: |
616 | return perf_session__process_sample(event, self, ops); | 675 | return ops->sample(event, sample, session); |
617 | case PERF_RECORD_MMAP: | 676 | case PERF_RECORD_MMAP: |
618 | return ops->mmap(event, self); | 677 | return ops->mmap(event, sample, session); |
619 | case PERF_RECORD_COMM: | 678 | case PERF_RECORD_COMM: |
620 | return ops->comm(event, self); | 679 | return ops->comm(event, sample, session); |
621 | case PERF_RECORD_FORK: | 680 | case PERF_RECORD_FORK: |
622 | return ops->fork(event, self); | 681 | return ops->fork(event, sample, session); |
623 | case PERF_RECORD_EXIT: | 682 | case PERF_RECORD_EXIT: |
624 | return ops->exit(event, self); | 683 | return ops->exit(event, sample, session); |
625 | case PERF_RECORD_LOST: | 684 | case PERF_RECORD_LOST: |
626 | return ops->lost(event, self); | 685 | return ops->lost(event, sample, session); |
627 | case PERF_RECORD_READ: | 686 | case PERF_RECORD_READ: |
628 | return ops->read(event, self); | 687 | return ops->read(event, sample, session); |
629 | case PERF_RECORD_THROTTLE: | 688 | case PERF_RECORD_THROTTLE: |
630 | return ops->throttle(event, self); | 689 | return ops->throttle(event, sample, session); |
631 | case PERF_RECORD_UNTHROTTLE: | 690 | case PERF_RECORD_UNTHROTTLE: |
632 | return ops->unthrottle(event, self); | 691 | return ops->unthrottle(event, sample, session); |
692 | default: | ||
693 | ++session->hists.stats.nr_unknown_events; | ||
694 | return -1; | ||
695 | } | ||
696 | } | ||
697 | |||
698 | static int perf_session__process_event(struct perf_session *session, | ||
699 | event_t *event, | ||
700 | struct perf_event_ops *ops, | ||
701 | u64 file_offset) | ||
702 | { | ||
703 | struct sample_data sample; | ||
704 | int ret; | ||
705 | |||
706 | trace_event(event); | ||
707 | |||
708 | if (session->header.needs_swap && event__swap_ops[event->header.type]) | ||
709 | event__swap_ops[event->header.type](event); | ||
710 | |||
711 | if (event->header.type >= PERF_RECORD_MMAP && | ||
712 | event->header.type <= PERF_RECORD_SAMPLE) { | ||
713 | event__parse_sample(event, session, &sample); | ||
714 | if (dump_trace) | ||
715 | perf_session__print_tstamp(session, event, &sample); | ||
716 | } | ||
717 | |||
718 | if (event->header.type < PERF_RECORD_HEADER_MAX) { | ||
719 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | ||
720 | file_offset, event->header.size, | ||
721 | event__name[event->header.type]); | ||
722 | hists__inc_nr_events(&session->hists, event->header.type); | ||
723 | } | ||
724 | |||
725 | /* These events are processed right away */ | ||
726 | switch (event->header.type) { | ||
727 | case PERF_RECORD_SAMPLE: | ||
728 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", | ||
729 | event->header.misc, | ||
730 | sample.pid, sample.tid, sample.ip, sample.period); | ||
731 | |||
732 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { | ||
733 | if (!ip_callchain__valid(sample.callchain, event)) { | ||
734 | pr_debug("call-chain problem with event, " | ||
735 | "skipping it.\n"); | ||
736 | ++session->hists.stats.nr_invalid_chains; | ||
737 | session->hists.stats.total_invalid_chains += | ||
738 | sample.period; | ||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | callchain__dump(&sample); | ||
743 | } | ||
744 | break; | ||
745 | |||
633 | case PERF_RECORD_HEADER_ATTR: | 746 | case PERF_RECORD_HEADER_ATTR: |
634 | return ops->attr(event, self); | 747 | return ops->attr(event, session); |
635 | case PERF_RECORD_HEADER_EVENT_TYPE: | 748 | case PERF_RECORD_HEADER_EVENT_TYPE: |
636 | return ops->event_type(event, self); | 749 | return ops->event_type(event, session); |
637 | case PERF_RECORD_HEADER_TRACING_DATA: | 750 | case PERF_RECORD_HEADER_TRACING_DATA: |
638 | /* setup for reading amidst mmap */ | 751 | /* setup for reading amidst mmap */ |
639 | lseek(self->fd, file_offset, SEEK_SET); | 752 | lseek(session->fd, file_offset, SEEK_SET); |
640 | return ops->tracing_data(event, self); | 753 | return ops->tracing_data(event, session); |
641 | case PERF_RECORD_HEADER_BUILD_ID: | 754 | case PERF_RECORD_HEADER_BUILD_ID: |
642 | return ops->build_id(event, self); | 755 | return ops->build_id(event, session); |
643 | case PERF_RECORD_FINISHED_ROUND: | 756 | case PERF_RECORD_FINISHED_ROUND: |
644 | return ops->finished_round(event, self, ops); | 757 | return ops->finished_round(event, session, ops); |
645 | default: | 758 | default: |
646 | ++self->hists.stats.nr_unknown_events; | 759 | break; |
647 | return -1; | ||
648 | } | 760 | } |
761 | |||
762 | if (ops->ordered_samples) { | ||
763 | ret = perf_session_queue_event(session, event, &sample); | ||
764 | if (ret != -ETIME) | ||
765 | return ret; | ||
766 | } | ||
767 | |||
768 | return perf_session_deliver_event(session, event, &sample, ops); | ||
649 | } | 769 | } |
650 | 770 | ||
651 | void perf_event_header__bswap(struct perf_event_header *self) | 771 | void perf_event_header__bswap(struct perf_event_header *self) |
@@ -894,6 +1014,14 @@ out_err: | |||
894 | session->hists.stats.nr_unknown_events); | 1014 | session->hists.stats.nr_unknown_events); |
895 | } | 1015 | } |
896 | 1016 | ||
1017 | if (session->hists.stats.nr_invalid_chains != 0) { | ||
1018 | ui__warning("Found invalid callchains!\n\n" | ||
1019 | "%u out of %u events were discarded for this reason.\n\n" | ||
1020 | "Consider reporting to linux-kernel@vger.kernel.org.\n\n", | ||
1021 | session->hists.stats.nr_invalid_chains, | ||
1022 | session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); | ||
1023 | } | ||
1024 | |||
897 | perf_session_free_sample_buffers(session); | 1025 | perf_session_free_sample_buffers(session); |
898 | return err; | 1026 | return err; |
899 | } | 1027 | } |