diff options
Diffstat (limited to '')
-rw-r--r-- | tools/perf/arch/x86/util/intel-pt.c | 231 |
1 files changed, 107 insertions, 124 deletions
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index 082e5f2a415a..6de7e2d21075 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -11,6 +11,7 @@ #include <linux/bitops.h> #include <linux/log2.h> #include <linux/zalloc.h> +#include <linux/err.h> #include <cpuid.h> #include "../../../util/session.h" @@ -22,7 +23,7 @@ #include "../../../util/mmap.h" #include <subcmd/parse-options.h> #include "../../../util/parse-events.h" -#include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include "../../../util/debug.h" #include "../../../util/auxtrace.h" #include "../../../util/perf_api_probe.h" @@ -59,43 +60,34 @@ struct intel_pt_recording { size_t priv_size; }; -static int intel_pt_parse_terms_with_default(const char *pmu_name, - struct list_head *formats, +static int intel_pt_parse_terms_with_default(const struct perf_pmu *pmu, const char *str, u64 *config) { - struct list_head *terms; + struct parse_events_terms terms; struct perf_event_attr attr = { .size = 0, }; int err; - terms = malloc(sizeof(struct list_head)); - if (!terms) - return -ENOMEM; - - INIT_LIST_HEAD(terms); - - err = parse_events_terms(terms, str); + parse_events_terms__init(&terms); + err = parse_events_terms(&terms, str, /*input=*/ NULL); if (err) goto out_free; attr.config = *config; - err = perf_pmu__config_terms(pmu_name, formats, &attr, terms, true, - NULL); + err = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/true, /*err=*/NULL); if (err) goto out_free; *config = attr.config; out_free: - parse_events_terms__delete(terms); + parse_events_terms__exit(&terms); return err; } -static int intel_pt_parse_terms(const char *pmu_name, struct list_head *formats, - const char *str, u64 *config) +static int intel_pt_parse_terms(const struct perf_pmu *pmu, const char *str, u64 *config) { *config = 0; - return intel_pt_parse_terms_with_default(pmu_name, formats, str, - config); + return intel_pt_parse_terms_with_default(pmu, str, config); } static u64 intel_pt_masked_bits(u64 mask, u64 bits) @@ -125,7 +117,7 @@ static int intel_pt_read_config(struct perf_pmu *intel_pt_pmu, const char *str, *res = 0; - mask = perf_pmu__format_bits(&intel_pt_pmu->format, str); + mask = perf_pmu__format_bits(intel_pt_pmu, str); if (!mask) return -EINVAL; @@ -185,7 +177,7 @@ static int intel_pt_pick_bit(int bits, int target) return pick; } -static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu) +static u64 intel_pt_default_config(const struct perf_pmu *intel_pt_pmu) { char buf[256]; int mtc, mtc_periods = 0, mtc_period; @@ -193,16 +185,19 @@ static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu) int pos = 0; u64 config; char c; + int dirfd; + + dirfd = perf_pmu__event_source_devices_fd(); pos += scnprintf(buf + pos, sizeof(buf) - pos, "tsc"); - if (perf_pmu__scan_file(intel_pt_pmu, "caps/mtc", "%d", - &mtc) != 1) + if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "caps/mtc", "%d", + &mtc) != 1) mtc = 1; if (mtc) { - if (perf_pmu__scan_file(intel_pt_pmu, "caps/mtc_periods", "%x", - &mtc_periods) != 1) + if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "caps/mtc_periods", "%x", + &mtc_periods) != 1) mtc_periods = 0; if (mtc_periods) { mtc_period = intel_pt_pick_bit(mtc_periods, 3); @@ -211,13 +206,13 @@ static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu) } } - if (perf_pmu__scan_file(intel_pt_pmu, "caps/psb_cyc", "%d", - &psb_cyc) != 1) + if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "caps/psb_cyc", "%d", + &psb_cyc) != 1) psb_cyc = 1; if (psb_cyc && mtc_periods) { - if (perf_pmu__scan_file(intel_pt_pmu, "caps/psb_periods", "%x", - &psb_periods) != 1) + if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "caps/psb_periods", "%x", + &psb_periods) != 1) psb_periods = 0; if (psb_periods) { psb_period = intel_pt_pick_bit(psb_periods, 3); @@ -226,15 +221,15 @@ static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu) } } - if (perf_pmu__scan_file(intel_pt_pmu, "format/pt", "%c", &c) == 1 && - perf_pmu__scan_file(intel_pt_pmu, "format/branch", "%c", &c) == 1) + if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "format/pt", "%c", &c) == 1 && + perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "format/branch", "%c", &c) == 1) pos += scnprintf(buf + pos, sizeof(buf) - pos, ",pt,branch"); pr_debug2("%s default config: %s\n", intel_pt_pmu->name, buf); - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, buf, - &config); + intel_pt_parse_terms(intel_pt_pmu, buf, &config); + close(dirfd); return config; } @@ -261,20 +256,17 @@ static int intel_pt_parse_snapshot_options(struct auxtrace_record *itr, return 0; } -struct perf_event_attr * -intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu) +void intel_pt_pmu_default_config(const struct perf_pmu *intel_pt_pmu, + struct perf_event_attr *attr) { - struct perf_event_attr *attr; + static u64 config; + static bool initialized; - attr = zalloc(sizeof(struct perf_event_attr)); - if (!attr) - return NULL; - - attr->config = intel_pt_default_config(intel_pt_pmu); - - intel_pt_pmu->selectable = true; - - return attr; + if (!initialized) { + config = intel_pt_default_config(intel_pt_pmu); + initialized = true; + } + attr->config = config; } static const char *intel_pt_find_filter(struct evlist *evlist, @@ -306,6 +298,7 @@ intel_pt_info_priv_size(struct auxtrace_record *itr, struct evlist *evlist) ptr->priv_size = (INTEL_PT_AUXTRACE_PRIV_MAX * sizeof(u64)) + intel_pt_filter_bytes(filter); + ptr->priv_size += sizeof(u64); /* Cap Event Trace */ return ptr->priv_size; } @@ -335,28 +328,27 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, unsigned long max_non_turbo_ratio; size_t filter_str_len; const char *filter; + int event_trace; __u64 *info; int err; if (priv_size != ptr->priv_size) return -EINVAL; - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "tsc", &tsc_bit); - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "noretcomp", &noretcomp_bit); - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "mtc", &mtc_bit); - mtc_freq_bits = perf_pmu__format_bits(&intel_pt_pmu->format, - "mtc_period"); - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "cyc", &cyc_bit); + intel_pt_parse_terms(intel_pt_pmu, "tsc", &tsc_bit); + intel_pt_parse_terms(intel_pt_pmu, "noretcomp", &noretcomp_bit); + intel_pt_parse_terms(intel_pt_pmu, "mtc", &mtc_bit); + mtc_freq_bits = perf_pmu__format_bits(intel_pt_pmu, "mtc_period"); + intel_pt_parse_terms(intel_pt_pmu, "cyc", &cyc_bit); intel_pt_tsc_ctc_ratio(&tsc_ctc_ratio_n, &tsc_ctc_ratio_d); if (perf_pmu__scan_file(intel_pt_pmu, "max_nonturbo_ratio", "%lu", &max_non_turbo_ratio) != 1) max_non_turbo_ratio = 0; + if (perf_pmu__scan_file(intel_pt_pmu, "caps/event_trace", + "%d", &event_trace) != 1) + event_trace = 0; filter = intel_pt_find_filter(session->evlist, ptr->intel_pt_pmu); filter_str_len = filter ? strlen(filter) : 0; @@ -377,7 +369,7 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, ui__warning("Intel Processor Trace: TSC not available\n"); } - per_cpu_mmaps = !perf_cpu_map__empty(session->evlist->core.cpus); + per_cpu_mmaps = !perf_cpu_map__is_any_cpu_or_is_empty(session->evlist->core.user_requested_cpus); auxtrace_info->type = PERF_AUXTRACE_INTEL_PT; auxtrace_info->priv[INTEL_PT_PMU_TYPE] = intel_pt_pmu->type; @@ -407,36 +399,34 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, info += len >> 3; } + *info++ = event_trace; + return 0; } +#ifdef HAVE_LIBTRACEEVENT static int intel_pt_track_switches(struct evlist *evlist) { const char *sched_switch = "sched:sched_switch"; struct evsel *evsel; int err; - if (!perf_evlist__can_select_event(evlist, sched_switch)) + if (!evlist__can_select_event(evlist, sched_switch)) return -EPERM; - err = parse_events(evlist, sched_switch, NULL); - if (err) { - pr_debug2("%s: failed to parse %s, error %d\n", + evsel = evlist__add_sched_switch(evlist, true); + if (IS_ERR(evsel)) { + err = PTR_ERR(evsel); + pr_debug2("%s: failed to create %s, error = %d\n", __func__, sched_switch, err); return err; } - evsel = evlist__last(evlist); - - evsel__set_sample_bit(evsel, CPU); - evsel__set_sample_bit(evsel, TIME); - - evsel->core.system_wide = true; - evsel->no_aux_samples = true; evsel->immediate = true; return 0; } +#endif static void intel_pt_valid_str(char *str, size_t len, u64 valid) { @@ -484,7 +474,7 @@ static void intel_pt_valid_str(char *str, size_t len, u64 valid) } } -static int intel_pt_val_config_term(struct perf_pmu *intel_pt_pmu, +static int intel_pt_val_config_term(struct perf_pmu *intel_pt_pmu, int dirfd, const char *caps, const char *name, const char *supported, u64 config) { @@ -494,16 +484,16 @@ static int intel_pt_val_config_term(struct perf_pmu *intel_pt_pmu, u64 bits; int ok; - if (perf_pmu__scan_file(intel_pt_pmu, caps, "%llx", &valid) != 1) + if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, caps, "%llx", &valid) != 1) valid = 0; if (supported && - perf_pmu__scan_file(intel_pt_pmu, supported, "%d", &ok) == 1 && !ok) + perf_pmu__scan_file_at(intel_pt_pmu, dirfd, supported, "%d", &ok) == 1 && !ok) valid = 0; valid |= 1; - bits = perf_pmu__format_bits(&intel_pt_pmu->format, name); + bits = perf_pmu__format_bits(intel_pt_pmu, name); config &= bits; @@ -527,56 +517,45 @@ out_err: static int intel_pt_validate_config(struct perf_pmu *intel_pt_pmu, struct evsel *evsel) { - int err; + int err, dirfd; char c; if (!evsel) return 0; + dirfd = perf_pmu__event_source_devices_fd(); + if (dirfd < 0) + return dirfd; + /* * If supported, force pass-through config term (pt=1) even if user * sets pt=0, which avoids senseless kernel errors. */ - if (perf_pmu__scan_file(intel_pt_pmu, "format/pt", "%c", &c) == 1 && + if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "format/pt", "%c", &c) == 1 && !(evsel->core.attr.config & 1)) { pr_warning("pt=0 doesn't make sense, forcing pt=1\n"); evsel->core.attr.config |= 1; } - err = intel_pt_val_config_term(intel_pt_pmu, "caps/cycle_thresholds", + err = intel_pt_val_config_term(intel_pt_pmu, dirfd, "caps/cycle_thresholds", "cyc_thresh", "caps/psb_cyc", evsel->core.attr.config); if (err) - return err; + goto out; - err = intel_pt_val_config_term(intel_pt_pmu, "caps/mtc_periods", + err = intel_pt_val_config_term(intel_pt_pmu, dirfd, "caps/mtc_periods", "mtc_period", "caps/mtc", evsel->core.attr.config); if (err) - return err; + goto out; - return intel_pt_val_config_term(intel_pt_pmu, "caps/psb_periods", + err = intel_pt_val_config_term(intel_pt_pmu, dirfd, "caps/psb_periods", "psb_period", "caps/psb_cyc", evsel->core.attr.config); -} - -static void intel_pt_config_sample_mode(struct perf_pmu *intel_pt_pmu, - struct evsel *evsel) -{ - u64 user_bits = 0, bits; - struct evsel_config_term *term = evsel__get_config_term(evsel, CFG_CHG); - - if (term) - user_bits = term->val.cfg_chg; - - bits = perf_pmu__format_bits(&intel_pt_pmu->format, "psb_period"); - /* Did user change psb_period */ - if (bits & user_bits) - return; - - /* Set psb_period to 0 */ - evsel->core.attr.config &= ~bits; +out: + close(dirfd); + return err; } static void intel_pt_min_max_sample_sz(struct evlist *evlist, @@ -625,7 +604,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, struct perf_pmu *intel_pt_pmu = ptr->intel_pt_pmu; bool have_timing_info, need_immediate = false; struct evsel *evsel, *intel_pt_evsel = NULL; - const struct perf_cpu_map *cpus = evlist->core.cpus; + const struct perf_cpu_map *cpus = evlist->core.user_requested_cpus; bool privileged = perf_event_paranoid_check(-1); u64 tsc_bit; int err; @@ -642,6 +621,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, evsel->core.attr.freq = 0; evsel->core.attr.sample_period = 1; evsel->no_aux_samples = true; + evsel->needs_auxtrace_mmap = true; intel_pt_evsel = evsel; opts->full_auxtrace = true; } @@ -669,7 +649,8 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, return 0; if (opts->auxtrace_sample_mode) - intel_pt_config_sample_mode(intel_pt_pmu, intel_pt_evsel); + evsel__set_config_if_unset(intel_pt_pmu, intel_pt_evsel, + "psb_period", 0); err = intel_pt_validate_config(intel_pt_pmu, intel_pt_evsel); if (err) @@ -776,8 +757,13 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, } } - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "tsc", &tsc_bit); + if (!opts->auxtrace_snapshot_mode && !opts->auxtrace_sample_mode) { + u32 aux_watermark = opts->auxtrace_mmap_pages * page_size / 4; + + intel_pt_evsel->core.attr.aux_watermark = aux_watermark; + } + + intel_pt_parse_terms(intel_pt_pmu, "tsc", &tsc_bit); if (opts->full_auxtrace && (intel_pt_evsel->core.attr.config & tsc_bit)) have_timing_info = true; @@ -788,7 +774,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, * Per-cpu recording needs sched_switch events to distinguish different * threads. */ - if (have_timing_info && !perf_cpu_map__empty(cpus) && + if (have_timing_info && !perf_cpu_map__is_any_cpu_or_is_empty(cpus) && !record_opts__no_switch_events(opts)) { if (perf_can_record_switch_events()) { bool cpu_wide = !target__none(&opts->target) && @@ -797,18 +783,11 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, if (!cpu_wide && perf_can_record_cpu_wide()) { struct evsel *switch_evsel; - err = parse_events(evlist, "dummy:u", NULL); - if (err) - return err; + switch_evsel = evlist__add_dummy_on_all_cpus(evlist); + if (!switch_evsel) + return -ENOMEM; - switch_evsel = evlist__last(evlist); - - switch_evsel->core.attr.freq = 0; - switch_evsel->core.attr.sample_period = 1; switch_evsel->core.attr.context_switch = 1; - - switch_evsel->core.system_wide = true; - switch_evsel->no_aux_samples = true; switch_evsel->immediate = true; evsel__set_sample_bit(switch_evsel, TID); @@ -827,6 +806,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, ptr->have_sched_switch = 2; } } else { +#ifdef HAVE_LIBTRACEEVENT err = intel_pt_track_switches(evlist); if (err == -EPERM) pr_debug2("Unable to select sched:sched_switch\n"); @@ -834,6 +814,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, return err; else ptr->have_sched_switch = 1; +#endif } } @@ -846,36 +827,38 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, * To obtain the auxtrace buffer file descriptor, the auxtrace * event must come first. */ - perf_evlist__to_front(evlist, intel_pt_evsel); + evlist__to_front(evlist, intel_pt_evsel); /* * In the case of per-cpu mmaps, we need the CPU on the * AUX event. */ - if (!perf_cpu_map__empty(cpus)) + if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) evsel__set_sample_bit(intel_pt_evsel, CPU); } /* Add dummy event to keep tracking */ if (opts->full_auxtrace) { + bool need_system_wide_tracking; struct evsel *tracking_evsel; - err = parse_events(evlist, "dummy:u", NULL); - if (err) - return err; - - tracking_evsel = evlist__last(evlist); + /* + * User space tasks can migrate between CPUs, so when tracing + * selected CPUs, sideband for all CPUs is still needed. + */ + need_system_wide_tracking = opts->target.cpu_list && + !intel_pt_evsel->core.attr.exclude_user; - perf_evlist__set_tracking_event(evlist, tracking_evsel); + tracking_evsel = evlist__add_aux_dummy(evlist, need_system_wide_tracking); + if (!tracking_evsel) + return -ENOMEM; - tracking_evsel->core.attr.freq = 0; - tracking_evsel->core.attr.sample_period = 1; + evlist__set_tracking_event(evlist, tracking_evsel); - tracking_evsel->no_aux_samples = true; if (need_immediate) tracking_evsel->immediate = true; /* In per-cpu case, always need the time of mmap events etc */ - if (!perf_cpu_map__empty(cpus)) { + if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) { evsel__set_sample_bit(tracking_evsel, TIME); /* And the CPU for switch events */ evsel__set_sample_bit(tracking_evsel, CPU); @@ -887,7 +870,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, * Warn the user when we do not have enough information to decode i.e. * per-cpu with no sched_switch (except workload-only). */ - if (!ptr->have_sched_switch && !perf_cpu_map__empty(cpus) && + if (!ptr->have_sched_switch && !perf_cpu_map__is_any_cpu_or_is_empty(cpus) && !target__none(&opts->target) && !intel_pt_evsel->core.attr.exclude_user) ui__warning("Intel Processor Trace decoding will not be possible except for kernel tracing!\n"); @@ -1183,7 +1166,7 @@ static u64 intel_pt_reference(struct auxtrace_record *itr __maybe_unused) struct auxtrace_record *intel_pt_recording_init(int *err) { - struct perf_pmu *intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME); + struct perf_pmu *intel_pt_pmu = perf_pmus__find(INTEL_PT_PMU_NAME); struct intel_pt_recording *ptr; if (!intel_pt_pmu) |