diff options
Diffstat (limited to 'tools/perf/util/session.c')
-rw-r--r-- | tools/perf/util/session.c | 300 |
1 files changed, 118 insertions, 182 deletions
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 88dfef70c13d..883406f4b381 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "util.h" | 14 | #include "util.h" |
15 | #include "cpumap.h" | 15 | #include "cpumap.h" |
16 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
17 | #include "asm/bug.h" | ||
17 | 18 | ||
18 | static int perf_session__open(struct perf_session *session) | 19 | static int perf_session__open(struct perf_session *session) |
19 | { | 20 | { |
@@ -66,6 +67,25 @@ static void perf_session__destroy_kernel_maps(struct perf_session *session) | |||
66 | machines__destroy_kernel_maps(&session->machines); | 67 | machines__destroy_kernel_maps(&session->machines); |
67 | } | 68 | } |
68 | 69 | ||
70 | static bool perf_session__has_comm_exec(struct perf_session *session) | ||
71 | { | ||
72 | struct perf_evsel *evsel; | ||
73 | |||
74 | evlist__for_each(session->evlist, evsel) { | ||
75 | if (evsel->attr.comm_exec) | ||
76 | return true; | ||
77 | } | ||
78 | |||
79 | return false; | ||
80 | } | ||
81 | |||
82 | static void perf_session__set_comm_exec(struct perf_session *session) | ||
83 | { | ||
84 | bool comm_exec = perf_session__has_comm_exec(session); | ||
85 | |||
86 | machines__set_comm_exec(&session->machines, comm_exec); | ||
87 | } | ||
88 | |||
69 | struct perf_session *perf_session__new(struct perf_data_file *file, | 89 | struct perf_session *perf_session__new(struct perf_data_file *file, |
70 | bool repipe, struct perf_tool *tool) | 90 | bool repipe, struct perf_tool *tool) |
71 | { | 91 | { |
@@ -75,9 +95,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
75 | goto out; | 95 | goto out; |
76 | 96 | ||
77 | session->repipe = repipe; | 97 | session->repipe = repipe; |
78 | INIT_LIST_HEAD(&session->ordered_samples.samples); | 98 | ordered_events__init(&session->ordered_events); |
79 | INIT_LIST_HEAD(&session->ordered_samples.sample_cache); | ||
80 | INIT_LIST_HEAD(&session->ordered_samples.to_free); | ||
81 | machines__init(&session->machines); | 99 | machines__init(&session->machines); |
82 | 100 | ||
83 | if (file) { | 101 | if (file) { |
@@ -91,6 +109,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
91 | goto out_close; | 109 | goto out_close; |
92 | 110 | ||
93 | perf_session__set_id_hdr_size(session); | 111 | perf_session__set_id_hdr_size(session); |
112 | perf_session__set_comm_exec(session); | ||
94 | } | 113 | } |
95 | } | 114 | } |
96 | 115 | ||
@@ -100,13 +119,13 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
100 | * kernel MMAP event, in perf_event__process_mmap(). | 119 | * kernel MMAP event, in perf_event__process_mmap(). |
101 | */ | 120 | */ |
102 | if (perf_session__create_kernel_maps(session) < 0) | 121 | if (perf_session__create_kernel_maps(session) < 0) |
103 | goto out_delete; | 122 | pr_warning("Cannot read kernel map\n"); |
104 | } | 123 | } |
105 | 124 | ||
106 | if (tool && tool->ordering_requires_timestamps && | 125 | if (tool && tool->ordering_requires_timestamps && |
107 | tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) { | 126 | tool->ordered_events && !perf_evlist__sample_id_all(session->evlist)) { |
108 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); | 127 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); |
109 | tool->ordered_samples = false; | 128 | tool->ordered_events = false; |
110 | } | 129 | } |
111 | 130 | ||
112 | return session; | 131 | return session; |
@@ -238,7 +257,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
238 | if (tool->build_id == NULL) | 257 | if (tool->build_id == NULL) |
239 | tool->build_id = process_finished_round_stub; | 258 | tool->build_id = process_finished_round_stub; |
240 | if (tool->finished_round == NULL) { | 259 | if (tool->finished_round == NULL) { |
241 | if (tool->ordered_samples) | 260 | if (tool->ordered_events) |
242 | tool->finished_round = process_finished_round; | 261 | tool->finished_round = process_finished_round; |
243 | else | 262 | else |
244 | tool->finished_round = process_finished_round_stub; | 263 | tool->finished_round = process_finished_round_stub; |
@@ -444,87 +463,6 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
444 | [PERF_RECORD_HEADER_MAX] = NULL, | 463 | [PERF_RECORD_HEADER_MAX] = NULL, |
445 | }; | 464 | }; |
446 | 465 | ||
447 | struct sample_queue { | ||
448 | u64 timestamp; | ||
449 | u64 file_offset; | ||
450 | union perf_event *event; | ||
451 | struct list_head list; | ||
452 | }; | ||
453 | |||
454 | static void perf_session_free_sample_buffers(struct perf_session *session) | ||
455 | { | ||
456 | struct ordered_samples *os = &session->ordered_samples; | ||
457 | |||
458 | while (!list_empty(&os->to_free)) { | ||
459 | struct sample_queue *sq; | ||
460 | |||
461 | sq = list_entry(os->to_free.next, struct sample_queue, list); | ||
462 | list_del(&sq->list); | ||
463 | free(sq); | ||
464 | } | ||
465 | } | ||
466 | |||
467 | static int perf_session_deliver_event(struct perf_session *session, | ||
468 | union perf_event *event, | ||
469 | struct perf_sample *sample, | ||
470 | struct perf_tool *tool, | ||
471 | u64 file_offset); | ||
472 | |||
473 | static int flush_sample_queue(struct perf_session *s, | ||
474 | struct perf_tool *tool) | ||
475 | { | ||
476 | struct ordered_samples *os = &s->ordered_samples; | ||
477 | struct list_head *head = &os->samples; | ||
478 | struct sample_queue *tmp, *iter; | ||
479 | struct perf_sample sample; | ||
480 | u64 limit = os->next_flush; | ||
481 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | ||
482 | bool show_progress = limit == ULLONG_MAX; | ||
483 | struct ui_progress prog; | ||
484 | int ret; | ||
485 | |||
486 | if (!tool->ordered_samples || !limit) | ||
487 | return 0; | ||
488 | |||
489 | if (show_progress) | ||
490 | ui_progress__init(&prog, os->nr_samples, "Processing time ordered events..."); | ||
491 | |||
492 | list_for_each_entry_safe(iter, tmp, head, list) { | ||
493 | if (session_done()) | ||
494 | return 0; | ||
495 | |||
496 | if (iter->timestamp > limit) | ||
497 | break; | ||
498 | |||
499 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample); | ||
500 | if (ret) | ||
501 | pr_err("Can't parse sample, err = %d\n", ret); | ||
502 | else { | ||
503 | ret = perf_session_deliver_event(s, iter->event, &sample, tool, | ||
504 | iter->file_offset); | ||
505 | if (ret) | ||
506 | return ret; | ||
507 | } | ||
508 | |||
509 | os->last_flush = iter->timestamp; | ||
510 | list_del(&iter->list); | ||
511 | list_add(&iter->list, &os->sample_cache); | ||
512 | os->nr_samples--; | ||
513 | |||
514 | if (show_progress) | ||
515 | ui_progress__update(&prog, 1); | ||
516 | } | ||
517 | |||
518 | if (list_empty(head)) { | ||
519 | os->last_sample = NULL; | ||
520 | } else if (last_ts <= limit) { | ||
521 | os->last_sample = | ||
522 | list_entry(head->prev, struct sample_queue, list); | ||
523 | } | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | /* | 466 | /* |
529 | * When perf record finishes a pass on every buffers, it records this pseudo | 467 | * When perf record finishes a pass on every buffers, it records this pseudo |
530 | * event. | 468 | * event. |
@@ -568,99 +506,43 @@ static int process_finished_round(struct perf_tool *tool, | |||
568 | union perf_event *event __maybe_unused, | 506 | union perf_event *event __maybe_unused, |
569 | struct perf_session *session) | 507 | struct perf_session *session) |
570 | { | 508 | { |
571 | int ret = flush_sample_queue(session, tool); | 509 | return ordered_events__flush(session, tool, OE_FLUSH__ROUND); |
572 | if (!ret) | ||
573 | session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; | ||
574 | |||
575 | return ret; | ||
576 | } | ||
577 | |||
578 | /* The queue is ordered by time */ | ||
579 | static void __queue_event(struct sample_queue *new, struct perf_session *s) | ||
580 | { | ||
581 | struct ordered_samples *os = &s->ordered_samples; | ||
582 | struct sample_queue *sample = os->last_sample; | ||
583 | u64 timestamp = new->timestamp; | ||
584 | struct list_head *p; | ||
585 | |||
586 | ++os->nr_samples; | ||
587 | os->last_sample = new; | ||
588 | |||
589 | if (!sample) { | ||
590 | list_add(&new->list, &os->samples); | ||
591 | os->max_timestamp = timestamp; | ||
592 | return; | ||
593 | } | ||
594 | |||
595 | /* | ||
596 | * last_sample might point to some random place in the list as it's | ||
597 | * the last queued event. We expect that the new event is close to | ||
598 | * this. | ||
599 | */ | ||
600 | if (sample->timestamp <= timestamp) { | ||
601 | while (sample->timestamp <= timestamp) { | ||
602 | p = sample->list.next; | ||
603 | if (p == &os->samples) { | ||
604 | list_add_tail(&new->list, &os->samples); | ||
605 | os->max_timestamp = timestamp; | ||
606 | return; | ||
607 | } | ||
608 | sample = list_entry(p, struct sample_queue, list); | ||
609 | } | ||
610 | list_add_tail(&new->list, &sample->list); | ||
611 | } else { | ||
612 | while (sample->timestamp > timestamp) { | ||
613 | p = sample->list.prev; | ||
614 | if (p == &os->samples) { | ||
615 | list_add(&new->list, &os->samples); | ||
616 | return; | ||
617 | } | ||
618 | sample = list_entry(p, struct sample_queue, list); | ||
619 | } | ||
620 | list_add(&new->list, &sample->list); | ||
621 | } | ||
622 | } | 510 | } |
623 | 511 | ||
624 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) | ||
625 | |||
626 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 512 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
627 | struct perf_sample *sample, u64 file_offset) | 513 | struct perf_tool *tool, struct perf_sample *sample, |
514 | u64 file_offset) | ||
628 | { | 515 | { |
629 | struct ordered_samples *os = &s->ordered_samples; | 516 | struct ordered_events *oe = &s->ordered_events; |
630 | struct list_head *sc = &os->sample_cache; | ||
631 | u64 timestamp = sample->time; | 517 | u64 timestamp = sample->time; |
632 | struct sample_queue *new; | 518 | struct ordered_event *new; |
633 | 519 | ||
634 | if (!timestamp || timestamp == ~0ULL) | 520 | if (!timestamp || timestamp == ~0ULL) |
635 | return -ETIME; | 521 | return -ETIME; |
636 | 522 | ||
637 | if (timestamp < s->ordered_samples.last_flush) { | 523 | if (timestamp < oe->last_flush) { |
638 | printf("Warning: Timestamp below last timeslice flush\n"); | 524 | WARN_ONCE(1, "Timestamp below last timeslice flush\n"); |
639 | return -EINVAL; | 525 | |
526 | pr_oe_time(timestamp, "out of order event"); | ||
527 | pr_oe_time(oe->last_flush, "last flush, last_flush_type %d\n", | ||
528 | oe->last_flush_type); | ||
529 | |||
530 | /* We could get out of order messages after forced flush. */ | ||
531 | if (oe->last_flush_type != OE_FLUSH__HALF) | ||
532 | return -EINVAL; | ||
640 | } | 533 | } |
641 | 534 | ||
642 | if (!list_empty(sc)) { | 535 | new = ordered_events__new(oe, timestamp); |
643 | new = list_entry(sc->next, struct sample_queue, list); | 536 | if (!new) { |
644 | list_del(&new->list); | 537 | ordered_events__flush(s, tool, OE_FLUSH__HALF); |
645 | } else if (os->sample_buffer) { | 538 | new = ordered_events__new(oe, timestamp); |
646 | new = os->sample_buffer + os->sample_buffer_idx; | ||
647 | if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER) | ||
648 | os->sample_buffer = NULL; | ||
649 | } else { | ||
650 | os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new)); | ||
651 | if (!os->sample_buffer) | ||
652 | return -ENOMEM; | ||
653 | list_add(&os->sample_buffer->list, &os->to_free); | ||
654 | os->sample_buffer_idx = 2; | ||
655 | new = os->sample_buffer + 1; | ||
656 | } | 539 | } |
657 | 540 | ||
658 | new->timestamp = timestamp; | 541 | if (!new) |
542 | return -ENOMEM; | ||
543 | |||
659 | new->file_offset = file_offset; | 544 | new->file_offset = file_offset; |
660 | new->event = event; | 545 | new->event = event; |
661 | |||
662 | __queue_event(new, s); | ||
663 | |||
664 | return 0; | 546 | return 0; |
665 | } | 547 | } |
666 | 548 | ||
@@ -920,11 +802,10 @@ perf_session__deliver_sample(struct perf_session *session, | |||
920 | &sample->read.one, machine); | 802 | &sample->read.one, machine); |
921 | } | 803 | } |
922 | 804 | ||
923 | static int perf_session_deliver_event(struct perf_session *session, | 805 | int perf_session__deliver_event(struct perf_session *session, |
924 | union perf_event *event, | 806 | union perf_event *event, |
925 | struct perf_sample *sample, | 807 | struct perf_sample *sample, |
926 | struct perf_tool *tool, | 808 | struct perf_tool *tool, u64 file_offset) |
927 | u64 file_offset) | ||
928 | { | 809 | { |
929 | struct perf_evsel *evsel; | 810 | struct perf_evsel *evsel; |
930 | struct machine *machine; | 811 | struct machine *machine; |
@@ -1005,8 +886,10 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
1005 | switch (event->header.type) { | 886 | switch (event->header.type) { |
1006 | case PERF_RECORD_HEADER_ATTR: | 887 | case PERF_RECORD_HEADER_ATTR: |
1007 | err = tool->attr(tool, event, &session->evlist); | 888 | err = tool->attr(tool, event, &session->evlist); |
1008 | if (err == 0) | 889 | if (err == 0) { |
1009 | perf_session__set_id_hdr_size(session); | 890 | perf_session__set_id_hdr_size(session); |
891 | perf_session__set_comm_exec(session); | ||
892 | } | ||
1010 | return err; | 893 | return err; |
1011 | case PERF_RECORD_HEADER_EVENT_TYPE: | 894 | case PERF_RECORD_HEADER_EVENT_TYPE: |
1012 | /* | 895 | /* |
@@ -1036,6 +919,61 @@ static void event_swap(union perf_event *event, bool sample_id_all) | |||
1036 | swap(event, sample_id_all); | 919 | swap(event, sample_id_all); |
1037 | } | 920 | } |
1038 | 921 | ||
922 | int perf_session__peek_event(struct perf_session *session, off_t file_offset, | ||
923 | void *buf, size_t buf_sz, | ||
924 | union perf_event **event_ptr, | ||
925 | struct perf_sample *sample) | ||
926 | { | ||
927 | union perf_event *event; | ||
928 | size_t hdr_sz, rest; | ||
929 | int fd; | ||
930 | |||
931 | if (session->one_mmap && !session->header.needs_swap) { | ||
932 | event = file_offset - session->one_mmap_offset + | ||
933 | session->one_mmap_addr; | ||
934 | goto out_parse_sample; | ||
935 | } | ||
936 | |||
937 | if (perf_data_file__is_pipe(session->file)) | ||
938 | return -1; | ||
939 | |||
940 | fd = perf_data_file__fd(session->file); | ||
941 | hdr_sz = sizeof(struct perf_event_header); | ||
942 | |||
943 | if (buf_sz < hdr_sz) | ||
944 | return -1; | ||
945 | |||
946 | if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 || | ||
947 | readn(fd, &buf, hdr_sz) != (ssize_t)hdr_sz) | ||
948 | return -1; | ||
949 | |||
950 | event = (union perf_event *)buf; | ||
951 | |||
952 | if (session->header.needs_swap) | ||
953 | perf_event_header__bswap(&event->header); | ||
954 | |||
955 | if (event->header.size < hdr_sz) | ||
956 | return -1; | ||
957 | |||
958 | rest = event->header.size - hdr_sz; | ||
959 | |||
960 | if (readn(fd, &buf, rest) != (ssize_t)rest) | ||
961 | return -1; | ||
962 | |||
963 | if (session->header.needs_swap) | ||
964 | event_swap(event, perf_evlist__sample_id_all(session->evlist)); | ||
965 | |||
966 | out_parse_sample: | ||
967 | |||
968 | if (sample && event->header.type < PERF_RECORD_USER_TYPE_START && | ||
969 | perf_evlist__parse_sample(session->evlist, event, sample)) | ||
970 | return -1; | ||
971 | |||
972 | *event_ptr = event; | ||
973 | |||
974 | return 0; | ||
975 | } | ||
976 | |||
1039 | static s64 perf_session__process_event(struct perf_session *session, | 977 | static s64 perf_session__process_event(struct perf_session *session, |
1040 | union perf_event *event, | 978 | union perf_event *event, |
1041 | struct perf_tool *tool, | 979 | struct perf_tool *tool, |
@@ -1062,15 +1000,15 @@ static s64 perf_session__process_event(struct perf_session *session, | |||
1062 | if (ret) | 1000 | if (ret) |
1063 | return ret; | 1001 | return ret; |
1064 | 1002 | ||
1065 | if (tool->ordered_samples) { | 1003 | if (tool->ordered_events) { |
1066 | ret = perf_session_queue_event(session, event, &sample, | 1004 | ret = perf_session_queue_event(session, event, tool, &sample, |
1067 | file_offset); | 1005 | file_offset); |
1068 | if (ret != -ETIME) | 1006 | if (ret != -ETIME) |
1069 | return ret; | 1007 | return ret; |
1070 | } | 1008 | } |
1071 | 1009 | ||
1072 | return perf_session_deliver_event(session, event, &sample, tool, | 1010 | return perf_session__deliver_event(session, event, &sample, tool, |
1073 | file_offset); | 1011 | file_offset); |
1074 | } | 1012 | } |
1075 | 1013 | ||
1076 | void perf_event_header__bswap(struct perf_event_header *hdr) | 1014 | void perf_event_header__bswap(struct perf_event_header *hdr) |
@@ -1222,12 +1160,11 @@ more: | |||
1222 | goto more; | 1160 | goto more; |
1223 | done: | 1161 | done: |
1224 | /* do the final flush for ordered samples */ | 1162 | /* do the final flush for ordered samples */ |
1225 | session->ordered_samples.next_flush = ULLONG_MAX; | 1163 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
1226 | err = flush_sample_queue(session, tool); | ||
1227 | out_err: | 1164 | out_err: |
1228 | free(buf); | 1165 | free(buf); |
1229 | perf_session__warn_about_errors(session, tool); | 1166 | perf_session__warn_about_errors(session, tool); |
1230 | perf_session_free_sample_buffers(session); | 1167 | ordered_events__free(&session->ordered_events); |
1231 | return err; | 1168 | return err; |
1232 | } | 1169 | } |
1233 | 1170 | ||
@@ -1368,12 +1305,11 @@ more: | |||
1368 | 1305 | ||
1369 | out: | 1306 | out: |
1370 | /* do the final flush for ordered samples */ | 1307 | /* do the final flush for ordered samples */ |
1371 | session->ordered_samples.next_flush = ULLONG_MAX; | 1308 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
1372 | err = flush_sample_queue(session, tool); | ||
1373 | out_err: | 1309 | out_err: |
1374 | ui_progress__finish(); | 1310 | ui_progress__finish(); |
1375 | perf_session__warn_about_errors(session, tool); | 1311 | perf_session__warn_about_errors(session, tool); |
1376 | perf_session_free_sample_buffers(session); | 1312 | ordered_events__free(&session->ordered_events); |
1377 | session->one_mmap = false; | 1313 | session->one_mmap = false; |
1378 | return err; | 1314 | return err; |
1379 | } | 1315 | } |