diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 146 |
1 files changed, 31 insertions, 115 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4869050e7194..44c6f3d55ce7 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -65,8 +65,9 @@ static int process_synthesized_event(struct perf_tool *tool, | |||
65 | return record__write(rec, event, event->header.size); | 65 | return record__write(rec, event, event->header.size); |
66 | } | 66 | } |
67 | 67 | ||
68 | static int record__mmap_read(struct record *rec, struct perf_mmap *md) | 68 | static int record__mmap_read(struct record *rec, int idx) |
69 | { | 69 | { |
70 | struct perf_mmap *md = &rec->evlist->mmap[idx]; | ||
70 | unsigned int head = perf_mmap__read_head(md); | 71 | unsigned int head = perf_mmap__read_head(md); |
71 | unsigned int old = md->prev; | 72 | unsigned int old = md->prev; |
72 | unsigned char *data = md->base + page_size; | 73 | unsigned char *data = md->base + page_size; |
@@ -102,8 +103,7 @@ static int record__mmap_read(struct record *rec, struct perf_mmap *md) | |||
102 | } | 103 | } |
103 | 104 | ||
104 | md->prev = old; | 105 | md->prev = old; |
105 | perf_mmap__write_tail(md, old); | 106 | perf_evlist__mmap_consume(rec->evlist, idx); |
106 | |||
107 | out: | 107 | out: |
108 | return rc; | 108 | return rc; |
109 | } | 109 | } |
@@ -161,7 +161,7 @@ try_again: | |||
161 | 161 | ||
162 | if (perf_evlist__apply_filters(evlist)) { | 162 | if (perf_evlist__apply_filters(evlist)) { |
163 | error("failed to set filter with %d (%s)\n", errno, | 163 | error("failed to set filter with %d (%s)\n", errno, |
164 | strerror(errno)); | 164 | strerror_r(errno, msg, sizeof(msg))); |
165 | rc = -1; | 165 | rc = -1; |
166 | goto out; | 166 | goto out; |
167 | } | 167 | } |
@@ -175,7 +175,8 @@ try_again: | |||
175 | "(current value: %u)\n", opts->mmap_pages); | 175 | "(current value: %u)\n", opts->mmap_pages); |
176 | rc = -errno; | 176 | rc = -errno; |
177 | } else { | 177 | } else { |
178 | pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 178 | pr_err("failed to mmap with %d (%s)\n", errno, |
179 | strerror_r(errno, msg, sizeof(msg))); | ||
179 | rc = -errno; | 180 | rc = -errno; |
180 | } | 181 | } |
181 | goto out; | 182 | goto out; |
@@ -244,7 +245,7 @@ static int record__mmap_read_all(struct record *rec) | |||
244 | 245 | ||
245 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { | 246 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { |
246 | if (rec->evlist->mmap[i].base) { | 247 | if (rec->evlist->mmap[i].base) { |
247 | if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) { | 248 | if (record__mmap_read(rec, i) != 0) { |
248 | rc = -1; | 249 | rc = -1; |
249 | goto out; | 250 | goto out; |
250 | } | 251 | } |
@@ -307,7 +308,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
307 | struct record_opts *opts = &rec->opts; | 308 | struct record_opts *opts = &rec->opts; |
308 | struct perf_data_file *file = &rec->file; | 309 | struct perf_data_file *file = &rec->file; |
309 | struct perf_session *session; | 310 | struct perf_session *session; |
310 | bool disabled = false; | 311 | bool disabled = false, draining = false; |
311 | 312 | ||
312 | rec->progname = argv[0]; | 313 | rec->progname = argv[0]; |
313 | 314 | ||
@@ -456,9 +457,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
456 | } | 457 | } |
457 | 458 | ||
458 | if (hits == rec->samples) { | 459 | if (hits == rec->samples) { |
459 | if (done) | 460 | if (done || draining) |
460 | break; | 461 | break; |
461 | err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1); | 462 | err = perf_evlist__poll(rec->evlist, -1); |
462 | /* | 463 | /* |
463 | * Propagate error, only if there's any. Ignore positive | 464 | * Propagate error, only if there's any. Ignore positive |
464 | * number of returned events and interrupt error. | 465 | * number of returned events and interrupt error. |
@@ -466,6 +467,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
466 | if (err > 0 || (err < 0 && errno == EINTR)) | 467 | if (err > 0 || (err < 0 && errno == EINTR)) |
467 | err = 0; | 468 | err = 0; |
468 | waking++; | 469 | waking++; |
470 | |||
471 | if (perf_evlist__filter_pollfd(rec->evlist, POLLERR | POLLHUP) == 0) | ||
472 | draining = true; | ||
469 | } | 473 | } |
470 | 474 | ||
471 | /* | 475 | /* |
@@ -480,7 +484,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
480 | } | 484 | } |
481 | 485 | ||
482 | if (forks && workload_exec_errno) { | 486 | if (forks && workload_exec_errno) { |
483 | char msg[512]; | 487 | char msg[STRERR_BUFSIZE]; |
484 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); | 488 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); |
485 | pr_err("Workload failed: %s\n", emsg); | 489 | pr_err("Workload failed: %s\n", emsg); |
486 | err = -1; | 490 | err = -1; |
@@ -620,145 +624,56 @@ error: | |||
620 | return ret; | 624 | return ret; |
621 | } | 625 | } |
622 | 626 | ||
623 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 627 | static void callchain_debug(void) |
624 | static int get_stack_size(char *str, unsigned long *_size) | ||
625 | { | ||
626 | char *endptr; | ||
627 | unsigned long size; | ||
628 | unsigned long max_size = round_down(USHRT_MAX, sizeof(u64)); | ||
629 | |||
630 | size = strtoul(str, &endptr, 0); | ||
631 | |||
632 | do { | ||
633 | if (*endptr) | ||
634 | break; | ||
635 | |||
636 | size = round_up(size, sizeof(u64)); | ||
637 | if (!size || size > max_size) | ||
638 | break; | ||
639 | |||
640 | *_size = size; | ||
641 | return 0; | ||
642 | |||
643 | } while (0); | ||
644 | |||
645 | pr_err("callchain: Incorrect stack dump size (max %ld): %s\n", | ||
646 | max_size, str); | ||
647 | return -1; | ||
648 | } | ||
649 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
650 | |||
651 | int record_parse_callchain(const char *arg, struct record_opts *opts) | ||
652 | { | ||
653 | char *tok, *name, *saveptr = NULL; | ||
654 | char *buf; | ||
655 | int ret = -1; | ||
656 | |||
657 | /* We need buffer that we know we can write to. */ | ||
658 | buf = malloc(strlen(arg) + 1); | ||
659 | if (!buf) | ||
660 | return -ENOMEM; | ||
661 | |||
662 | strcpy(buf, arg); | ||
663 | |||
664 | tok = strtok_r((char *)buf, ",", &saveptr); | ||
665 | name = tok ? : (char *)buf; | ||
666 | |||
667 | do { | ||
668 | /* Framepointer style */ | ||
669 | if (!strncmp(name, "fp", sizeof("fp"))) { | ||
670 | if (!strtok_r(NULL, ",", &saveptr)) { | ||
671 | opts->call_graph = CALLCHAIN_FP; | ||
672 | ret = 0; | ||
673 | } else | ||
674 | pr_err("callchain: No more arguments " | ||
675 | "needed for -g fp\n"); | ||
676 | break; | ||
677 | |||
678 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
679 | /* Dwarf style */ | ||
680 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { | ||
681 | const unsigned long default_stack_dump_size = 8192; | ||
682 | |||
683 | ret = 0; | ||
684 | opts->call_graph = CALLCHAIN_DWARF; | ||
685 | opts->stack_dump_size = default_stack_dump_size; | ||
686 | |||
687 | tok = strtok_r(NULL, ",", &saveptr); | ||
688 | if (tok) { | ||
689 | unsigned long size = 0; | ||
690 | |||
691 | ret = get_stack_size(tok, &size); | ||
692 | opts->stack_dump_size = size; | ||
693 | } | ||
694 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
695 | } else { | ||
696 | pr_err("callchain: Unknown --call-graph option " | ||
697 | "value: %s\n", arg); | ||
698 | break; | ||
699 | } | ||
700 | |||
701 | } while (0); | ||
702 | |||
703 | free(buf); | ||
704 | return ret; | ||
705 | } | ||
706 | |||
707 | static void callchain_debug(struct record_opts *opts) | ||
708 | { | 628 | { |
709 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; | 629 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; |
710 | 630 | ||
711 | pr_debug("callchain: type %s\n", str[opts->call_graph]); | 631 | pr_debug("callchain: type %s\n", str[callchain_param.record_mode]); |
712 | 632 | ||
713 | if (opts->call_graph == CALLCHAIN_DWARF) | 633 | if (callchain_param.record_mode == CALLCHAIN_DWARF) |
714 | pr_debug("callchain: stack dump size %d\n", | 634 | pr_debug("callchain: stack dump size %d\n", |
715 | opts->stack_dump_size); | 635 | callchain_param.dump_size); |
716 | } | 636 | } |
717 | 637 | ||
718 | int record_parse_callchain_opt(const struct option *opt, | 638 | int record_parse_callchain_opt(const struct option *opt __maybe_unused, |
719 | const char *arg, | 639 | const char *arg, |
720 | int unset) | 640 | int unset) |
721 | { | 641 | { |
722 | struct record_opts *opts = opt->value; | ||
723 | int ret; | 642 | int ret; |
724 | 643 | ||
725 | opts->call_graph_enabled = !unset; | 644 | callchain_param.enabled = !unset; |
726 | 645 | ||
727 | /* --no-call-graph */ | 646 | /* --no-call-graph */ |
728 | if (unset) { | 647 | if (unset) { |
729 | opts->call_graph = CALLCHAIN_NONE; | 648 | callchain_param.record_mode = CALLCHAIN_NONE; |
730 | pr_debug("callchain: disabled\n"); | 649 | pr_debug("callchain: disabled\n"); |
731 | return 0; | 650 | return 0; |
732 | } | 651 | } |
733 | 652 | ||
734 | ret = record_parse_callchain(arg, opts); | 653 | ret = parse_callchain_record_opt(arg); |
735 | if (!ret) | 654 | if (!ret) |
736 | callchain_debug(opts); | 655 | callchain_debug(); |
737 | 656 | ||
738 | return ret; | 657 | return ret; |
739 | } | 658 | } |
740 | 659 | ||
741 | int record_callchain_opt(const struct option *opt, | 660 | int record_callchain_opt(const struct option *opt __maybe_unused, |
742 | const char *arg __maybe_unused, | 661 | const char *arg __maybe_unused, |
743 | int unset __maybe_unused) | 662 | int unset __maybe_unused) |
744 | { | 663 | { |
745 | struct record_opts *opts = opt->value; | 664 | callchain_param.enabled = true; |
746 | 665 | ||
747 | opts->call_graph_enabled = !unset; | 666 | if (callchain_param.record_mode == CALLCHAIN_NONE) |
667 | callchain_param.record_mode = CALLCHAIN_FP; | ||
748 | 668 | ||
749 | if (opts->call_graph == CALLCHAIN_NONE) | 669 | callchain_debug(); |
750 | opts->call_graph = CALLCHAIN_FP; | ||
751 | |||
752 | callchain_debug(opts); | ||
753 | return 0; | 670 | return 0; |
754 | } | 671 | } |
755 | 672 | ||
756 | static int perf_record_config(const char *var, const char *value, void *cb) | 673 | static int perf_record_config(const char *var, const char *value, void *cb) |
757 | { | 674 | { |
758 | struct record *rec = cb; | ||
759 | |||
760 | if (!strcmp(var, "record.call-graph")) | 675 | if (!strcmp(var, "record.call-graph")) |
761 | return record_parse_callchain(value, &rec->opts); | 676 | var = "call-graph.record-mode"; /* fall-through */ |
762 | 677 | ||
763 | return perf_default_config(var, value, cb); | 678 | return perf_default_config(var, value, cb); |
764 | } | 679 | } |
@@ -781,6 +696,7 @@ static const char * const record_usage[] = { | |||
781 | */ | 696 | */ |
782 | static struct record record = { | 697 | static struct record record = { |
783 | .opts = { | 698 | .opts = { |
699 | .sample_time = true, | ||
784 | .mmap_pages = UINT_MAX, | 700 | .mmap_pages = UINT_MAX, |
785 | .user_freq = UINT_MAX, | 701 | .user_freq = UINT_MAX, |
786 | .user_interval = ULLONG_MAX, | 702 | .user_interval = ULLONG_MAX, |
@@ -907,7 +823,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
907 | usage_with_options(record_usage, record_options); | 823 | usage_with_options(record_usage, record_options); |
908 | } | 824 | } |
909 | 825 | ||
910 | symbol__init(); | 826 | symbol__init(NULL); |
911 | 827 | ||
912 | if (symbol_conf.kptr_restrict) | 828 | if (symbol_conf.kptr_restrict) |
913 | pr_warning( | 829 | pr_warning( |