diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 152 |
1 files changed, 121 insertions, 31 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 227b6ae9978..be4e1eee782 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -44,6 +44,7 @@ struct perf_record { | |||
44 | struct perf_evlist *evlist; | 44 | struct perf_evlist *evlist; |
45 | struct perf_session *session; | 45 | struct perf_session *session; |
46 | const char *progname; | 46 | const char *progname; |
47 | const char *uid_str; | ||
47 | int output; | 48 | int output; |
48 | unsigned int page_size; | 49 | unsigned int page_size; |
49 | int realtime_prio; | 50 | int realtime_prio; |
@@ -208,7 +209,7 @@ fallback_missing_features: | |||
208 | if (opts->exclude_guest_missing) | 209 | if (opts->exclude_guest_missing) |
209 | attr->exclude_guest = attr->exclude_host = 0; | 210 | attr->exclude_guest = attr->exclude_host = 0; |
210 | retry_sample_id: | 211 | retry_sample_id: |
211 | attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0; | 212 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; |
212 | try_again: | 213 | try_again: |
213 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, | 214 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, |
214 | opts->group, group_fd) < 0) { | 215 | opts->group, group_fd) < 0) { |
@@ -227,11 +228,11 @@ try_again: | |||
227 | "guest or host samples.\n"); | 228 | "guest or host samples.\n"); |
228 | opts->exclude_guest_missing = true; | 229 | opts->exclude_guest_missing = true; |
229 | goto fallback_missing_features; | 230 | goto fallback_missing_features; |
230 | } else if (opts->sample_id_all_avail) { | 231 | } else if (!opts->sample_id_all_missing) { |
231 | /* | 232 | /* |
232 | * Old kernel, no attr->sample_id_type_all field | 233 | * Old kernel, no attr->sample_id_type_all field |
233 | */ | 234 | */ |
234 | opts->sample_id_all_avail = false; | 235 | opts->sample_id_all_missing = true; |
235 | if (!opts->sample_time && !opts->raw_samples && !time_needed) | 236 | if (!opts->sample_time && !opts->raw_samples && !time_needed) |
236 | attr->sample_type &= ~PERF_SAMPLE_TIME; | 237 | attr->sample_type &= ~PERF_SAMPLE_TIME; |
237 | 238 | ||
@@ -396,7 +397,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
396 | { | 397 | { |
397 | struct stat st; | 398 | struct stat st; |
398 | int flags; | 399 | int flags; |
399 | int err, output; | 400 | int err, output, feat; |
400 | unsigned long waking = 0; | 401 | unsigned long waking = 0; |
401 | const bool forks = argc > 0; | 402 | const bool forks = argc > 0; |
402 | struct machine *machine; | 403 | struct machine *machine; |
@@ -463,8 +464,17 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
463 | 464 | ||
464 | rec->session = session; | 465 | rec->session = session; |
465 | 466 | ||
466 | if (!rec->no_buildid) | 467 | for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) |
467 | perf_header__set_feat(&session->header, HEADER_BUILD_ID); | 468 | perf_header__set_feat(&session->header, feat); |
469 | |||
470 | if (rec->no_buildid) | ||
471 | perf_header__clear_feat(&session->header, HEADER_BUILD_ID); | ||
472 | |||
473 | if (!have_tracepoints(&evsel_list->entries)) | ||
474 | perf_header__clear_feat(&session->header, HEADER_TRACE_INFO); | ||
475 | |||
476 | if (!rec->opts.branch_stack) | ||
477 | perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); | ||
468 | 478 | ||
469 | if (!rec->file_new) { | 479 | if (!rec->file_new) { |
470 | err = perf_session__read_header(session, output); | 480 | err = perf_session__read_header(session, output); |
@@ -472,22 +482,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
472 | goto out_delete_session; | 482 | goto out_delete_session; |
473 | } | 483 | } |
474 | 484 | ||
475 | if (have_tracepoints(&evsel_list->entries)) | ||
476 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | ||
477 | |||
478 | perf_header__set_feat(&session->header, HEADER_HOSTNAME); | ||
479 | perf_header__set_feat(&session->header, HEADER_OSRELEASE); | ||
480 | perf_header__set_feat(&session->header, HEADER_ARCH); | ||
481 | perf_header__set_feat(&session->header, HEADER_CPUDESC); | ||
482 | perf_header__set_feat(&session->header, HEADER_NRCPUS); | ||
483 | perf_header__set_feat(&session->header, HEADER_EVENT_DESC); | ||
484 | perf_header__set_feat(&session->header, HEADER_CMDLINE); | ||
485 | perf_header__set_feat(&session->header, HEADER_VERSION); | ||
486 | perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); | ||
487 | perf_header__set_feat(&session->header, HEADER_TOTAL_MEM); | ||
488 | perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY); | ||
489 | perf_header__set_feat(&session->header, HEADER_CPUID); | ||
490 | |||
491 | if (forks) { | 485 | if (forks) { |
492 | err = perf_evlist__prepare_workload(evsel_list, opts, argv); | 486 | err = perf_evlist__prepare_workload(evsel_list, opts, argv); |
493 | if (err < 0) { | 487 | if (err < 0) { |
@@ -647,6 +641,90 @@ out_delete_session: | |||
647 | return err; | 641 | return err; |
648 | } | 642 | } |
649 | 643 | ||
644 | #define BRANCH_OPT(n, m) \ | ||
645 | { .name = n, .mode = (m) } | ||
646 | |||
647 | #define BRANCH_END { .name = NULL } | ||
648 | |||
649 | struct branch_mode { | ||
650 | const char *name; | ||
651 | int mode; | ||
652 | }; | ||
653 | |||
654 | static const struct branch_mode branch_modes[] = { | ||
655 | BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER), | ||
656 | BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL), | ||
657 | BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV), | ||
658 | BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY), | ||
659 | BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL), | ||
660 | BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN), | ||
661 | BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL), | ||
662 | BRANCH_END | ||
663 | }; | ||
664 | |||
665 | static int | ||
666 | parse_branch_stack(const struct option *opt, const char *str, int unset) | ||
667 | { | ||
668 | #define ONLY_PLM \ | ||
669 | (PERF_SAMPLE_BRANCH_USER |\ | ||
670 | PERF_SAMPLE_BRANCH_KERNEL |\ | ||
671 | PERF_SAMPLE_BRANCH_HV) | ||
672 | |||
673 | uint64_t *mode = (uint64_t *)opt->value; | ||
674 | const struct branch_mode *br; | ||
675 | char *s, *os = NULL, *p; | ||
676 | int ret = -1; | ||
677 | |||
678 | if (unset) | ||
679 | return 0; | ||
680 | |||
681 | /* | ||
682 | * cannot set it twice, -b + --branch-filter for instance | ||
683 | */ | ||
684 | if (*mode) | ||
685 | return -1; | ||
686 | |||
687 | /* str may be NULL in case no arg is passed to -b */ | ||
688 | if (str) { | ||
689 | /* because str is read-only */ | ||
690 | s = os = strdup(str); | ||
691 | if (!s) | ||
692 | return -1; | ||
693 | |||
694 | for (;;) { | ||
695 | p = strchr(s, ','); | ||
696 | if (p) | ||
697 | *p = '\0'; | ||
698 | |||
699 | for (br = branch_modes; br->name; br++) { | ||
700 | if (!strcasecmp(s, br->name)) | ||
701 | break; | ||
702 | } | ||
703 | if (!br->name) { | ||
704 | ui__warning("unknown branch filter %s," | ||
705 | " check man page\n", s); | ||
706 | goto error; | ||
707 | } | ||
708 | |||
709 | *mode |= br->mode; | ||
710 | |||
711 | if (!p) | ||
712 | break; | ||
713 | |||
714 | s = p + 1; | ||
715 | } | ||
716 | } | ||
717 | ret = 0; | ||
718 | |||
719 | /* default to any branch */ | ||
720 | if ((*mode & ~ONLY_PLM) == 0) { | ||
721 | *mode = PERF_SAMPLE_BRANCH_ANY; | ||
722 | } | ||
723 | error: | ||
724 | free(os); | ||
725 | return ret; | ||
726 | } | ||
727 | |||
650 | static const char * const record_usage[] = { | 728 | static const char * const record_usage[] = { |
651 | "perf record [<options>] [<command>]", | 729 | "perf record [<options>] [<command>]", |
652 | "perf record [<options>] -- <command> [<options>]", | 730 | "perf record [<options>] -- <command> [<options>]", |
@@ -665,13 +743,10 @@ static const char * const record_usage[] = { | |||
665 | */ | 743 | */ |
666 | static struct perf_record record = { | 744 | static struct perf_record record = { |
667 | .opts = { | 745 | .opts = { |
668 | .target_pid = -1, | ||
669 | .target_tid = -1, | ||
670 | .mmap_pages = UINT_MAX, | 746 | .mmap_pages = UINT_MAX, |
671 | .user_freq = UINT_MAX, | 747 | .user_freq = UINT_MAX, |
672 | .user_interval = ULLONG_MAX, | 748 | .user_interval = ULLONG_MAX, |
673 | .freq = 1000, | 749 | .freq = 1000, |
674 | .sample_id_all_avail = true, | ||
675 | }, | 750 | }, |
676 | .write_mode = WRITE_FORCE, | 751 | .write_mode = WRITE_FORCE, |
677 | .file_new = true, | 752 | .file_new = true, |
@@ -690,9 +765,9 @@ const struct option record_options[] = { | |||
690 | parse_events_option), | 765 | parse_events_option), |
691 | OPT_CALLBACK(0, "filter", &record.evlist, "filter", | 766 | OPT_CALLBACK(0, "filter", &record.evlist, "filter", |
692 | "event filter", parse_filter), | 767 | "event filter", parse_filter), |
693 | OPT_INTEGER('p', "pid", &record.opts.target_pid, | 768 | OPT_STRING('p', "pid", &record.opts.target_pid, "pid", |
694 | "record events on existing process id"), | 769 | "record events on existing process id"), |
695 | OPT_INTEGER('t', "tid", &record.opts.target_tid, | 770 | OPT_STRING('t', "tid", &record.opts.target_tid, "tid", |
696 | "record events on existing thread id"), | 771 | "record events on existing thread id"), |
697 | OPT_INTEGER('r', "realtime", &record.realtime_prio, | 772 | OPT_INTEGER('r', "realtime", &record.realtime_prio, |
698 | "collect data with this RT SCHED_FIFO priority"), | 773 | "collect data with this RT SCHED_FIFO priority"), |
@@ -738,6 +813,15 @@ const struct option record_options[] = { | |||
738 | OPT_CALLBACK('G', "cgroup", &record.evlist, "name", | 813 | OPT_CALLBACK('G', "cgroup", &record.evlist, "name", |
739 | "monitor event in cgroup name only", | 814 | "monitor event in cgroup name only", |
740 | parse_cgroups), | 815 | parse_cgroups), |
816 | OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"), | ||
817 | |||
818 | OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack, | ||
819 | "branch any", "sample any taken branches", | ||
820 | parse_branch_stack), | ||
821 | |||
822 | OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack, | ||
823 | "branch filter mask", "branch stack filter modes", | ||
824 | parse_branch_stack), | ||
741 | OPT_END() | 825 | OPT_END() |
742 | }; | 826 | }; |
743 | 827 | ||
@@ -758,8 +842,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
758 | 842 | ||
759 | argc = parse_options(argc, argv, record_options, record_usage, | 843 | argc = parse_options(argc, argv, record_options, record_usage, |
760 | PARSE_OPT_STOP_AT_NON_OPTION); | 844 | PARSE_OPT_STOP_AT_NON_OPTION); |
761 | if (!argc && rec->opts.target_pid == -1 && rec->opts.target_tid == -1 && | 845 | if (!argc && !rec->opts.target_pid && !rec->opts.target_tid && |
762 | !rec->opts.system_wide && !rec->opts.cpu_list) | 846 | !rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str) |
763 | usage_with_options(record_usage, record_options); | 847 | usage_with_options(record_usage, record_options); |
764 | 848 | ||
765 | if (rec->force && rec->append_file) { | 849 | if (rec->force && rec->append_file) { |
@@ -799,11 +883,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
799 | goto out_symbol_exit; | 883 | goto out_symbol_exit; |
800 | } | 884 | } |
801 | 885 | ||
802 | if (rec->opts.target_pid != -1) | 886 | rec->opts.uid = parse_target_uid(rec->uid_str, rec->opts.target_tid, |
887 | rec->opts.target_pid); | ||
888 | if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1) | ||
889 | goto out_free_fd; | ||
890 | |||
891 | if (rec->opts.target_pid) | ||
803 | rec->opts.target_tid = rec->opts.target_pid; | 892 | rec->opts.target_tid = rec->opts.target_pid; |
804 | 893 | ||
805 | if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid, | 894 | if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid, |
806 | rec->opts.target_tid, rec->opts.cpu_list) < 0) | 895 | rec->opts.target_tid, rec->opts.uid, |
896 | rec->opts.cpu_list) < 0) | ||
807 | usage_with_options(record_usage, record_options); | 897 | usage_with_options(record_usage, record_options); |
808 | 898 | ||
809 | list_for_each_entry(pos, &evsel_list->entries, node) { | 899 | list_for_each_entry(pos, &evsel_list->entries, node) { |