162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * intel_pt.c: Intel Processor Trace support 462306a36Sopenharmony_ci * Copyright (c) 2013-2015, Intel Corporation. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <errno.h> 862306a36Sopenharmony_ci#include <stdbool.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/bitops.h> 1262306a36Sopenharmony_ci#include <linux/log2.h> 1362306a36Sopenharmony_ci#include <linux/zalloc.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <cpuid.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "../../../util/session.h" 1862306a36Sopenharmony_ci#include "../../../util/event.h" 1962306a36Sopenharmony_ci#include "../../../util/evlist.h" 2062306a36Sopenharmony_ci#include "../../../util/evsel.h" 2162306a36Sopenharmony_ci#include "../../../util/evsel_config.h" 2262306a36Sopenharmony_ci#include "../../../util/cpumap.h" 2362306a36Sopenharmony_ci#include "../../../util/mmap.h" 2462306a36Sopenharmony_ci#include <subcmd/parse-options.h> 2562306a36Sopenharmony_ci#include "../../../util/parse-events.h" 2662306a36Sopenharmony_ci#include "../../../util/pmus.h" 2762306a36Sopenharmony_ci#include "../../../util/debug.h" 2862306a36Sopenharmony_ci#include "../../../util/auxtrace.h" 2962306a36Sopenharmony_ci#include "../../../util/perf_api_probe.h" 3062306a36Sopenharmony_ci#include "../../../util/record.h" 3162306a36Sopenharmony_ci#include "../../../util/target.h" 3262306a36Sopenharmony_ci#include "../../../util/tsc.h" 3362306a36Sopenharmony_ci#include <internal/lib.h> // page_size 3462306a36Sopenharmony_ci#include "../../../util/intel-pt.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define KiB(x) ((x) * 1024) 3762306a36Sopenharmony_ci#define MiB(x) ((x) * 1024 * 1024) 3862306a36Sopenharmony_ci#define KiB_MASK(x) (KiB(x) - 1) 3962306a36Sopenharmony_ci#define MiB_MASK(x) (MiB(x) - 1) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define INTEL_PT_PSB_PERIOD_NEAR 256 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct intel_pt_snapshot_ref { 4462306a36Sopenharmony_ci void *ref_buf; 4562306a36Sopenharmony_ci size_t ref_offset; 4662306a36Sopenharmony_ci bool wrapped; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct intel_pt_recording { 5062306a36Sopenharmony_ci struct auxtrace_record itr; 5162306a36Sopenharmony_ci struct perf_pmu *intel_pt_pmu; 5262306a36Sopenharmony_ci int have_sched_switch; 5362306a36Sopenharmony_ci struct evlist *evlist; 5462306a36Sopenharmony_ci bool snapshot_mode; 5562306a36Sopenharmony_ci bool snapshot_init_done; 5662306a36Sopenharmony_ci size_t snapshot_size; 5762306a36Sopenharmony_ci size_t snapshot_ref_buf_size; 5862306a36Sopenharmony_ci int snapshot_ref_cnt; 5962306a36Sopenharmony_ci struct intel_pt_snapshot_ref *snapshot_refs; 6062306a36Sopenharmony_ci size_t priv_size; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int intel_pt_parse_terms_with_default(struct perf_pmu *pmu, 6462306a36Sopenharmony_ci const char *str, 6562306a36Sopenharmony_ci u64 *config) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct list_head *terms; 6862306a36Sopenharmony_ci struct perf_event_attr attr = { .size = 0, }; 6962306a36Sopenharmony_ci int err; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci terms = malloc(sizeof(struct list_head)); 7262306a36Sopenharmony_ci if (!terms) 7362306a36Sopenharmony_ci return -ENOMEM; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci INIT_LIST_HEAD(terms); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci err = parse_events_terms(terms, str, /*input=*/ NULL); 7862306a36Sopenharmony_ci if (err) 7962306a36Sopenharmony_ci goto out_free; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci attr.config = *config; 8262306a36Sopenharmony_ci err = perf_pmu__config_terms(pmu, &attr, terms, /*zero=*/true, /*err=*/NULL); 8362306a36Sopenharmony_ci if (err) 8462306a36Sopenharmony_ci goto out_free; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci *config = attr.config; 8762306a36Sopenharmony_ciout_free: 8862306a36Sopenharmony_ci parse_events_terms__delete(terms); 8962306a36Sopenharmony_ci return err; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int intel_pt_parse_terms(struct perf_pmu *pmu, const char *str, u64 *config) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci *config = 0; 9562306a36Sopenharmony_ci return intel_pt_parse_terms_with_default(pmu, str, config); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic u64 intel_pt_masked_bits(u64 mask, u64 bits) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci const u64 top_bit = 1ULL << 63; 10162306a36Sopenharmony_ci u64 res = 0; 10262306a36Sopenharmony_ci int i; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci for (i = 0; i < 64; i++) { 10562306a36Sopenharmony_ci if (mask & top_bit) { 10662306a36Sopenharmony_ci res <<= 1; 10762306a36Sopenharmony_ci if (bits & top_bit) 10862306a36Sopenharmony_ci res |= 1; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci mask <<= 1; 11162306a36Sopenharmony_ci bits <<= 1; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return res; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int intel_pt_read_config(struct perf_pmu *intel_pt_pmu, const char *str, 11862306a36Sopenharmony_ci struct evlist *evlist, u64 *res) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct evsel *evsel; 12162306a36Sopenharmony_ci u64 mask; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci *res = 0; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci mask = perf_pmu__format_bits(intel_pt_pmu, str); 12662306a36Sopenharmony_ci if (!mask) 12762306a36Sopenharmony_ci return -EINVAL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 13062306a36Sopenharmony_ci if (evsel->core.attr.type == intel_pt_pmu->type) { 13162306a36Sopenharmony_ci *res = intel_pt_masked_bits(mask, evsel->core.attr.config); 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic size_t intel_pt_psb_period(struct perf_pmu *intel_pt_pmu, 14062306a36Sopenharmony_ci struct evlist *evlist) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci u64 val; 14362306a36Sopenharmony_ci int err, topa_multiple_entries; 14462306a36Sopenharmony_ci size_t psb_period; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (perf_pmu__scan_file(intel_pt_pmu, "caps/topa_multiple_entries", 14762306a36Sopenharmony_ci "%d", &topa_multiple_entries) != 1) 14862306a36Sopenharmony_ci topa_multiple_entries = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * Use caps/topa_multiple_entries to indicate early hardware that had 15262306a36Sopenharmony_ci * extra frequent PSBs. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci if (!topa_multiple_entries) { 15562306a36Sopenharmony_ci psb_period = 256; 15662306a36Sopenharmony_ci goto out; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci err = intel_pt_read_config(intel_pt_pmu, "psb_period", evlist, &val); 16062306a36Sopenharmony_ci if (err) 16162306a36Sopenharmony_ci val = 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci psb_period = 1 << (val + 11); 16462306a36Sopenharmony_ciout: 16562306a36Sopenharmony_ci pr_debug2("%s psb_period %zu\n", intel_pt_pmu->name, psb_period); 16662306a36Sopenharmony_ci return psb_period; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int intel_pt_pick_bit(int bits, int target) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int pos, pick = -1; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for (pos = 0; bits; bits >>= 1, pos++) { 17462306a36Sopenharmony_ci if (bits & 1) { 17562306a36Sopenharmony_ci if (pos <= target || pick < 0) 17662306a36Sopenharmony_ci pick = pos; 17762306a36Sopenharmony_ci if (pos >= target) 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return pick; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci char buf[256]; 18862306a36Sopenharmony_ci int mtc, mtc_periods = 0, mtc_period; 18962306a36Sopenharmony_ci int psb_cyc, psb_periods, psb_period; 19062306a36Sopenharmony_ci int pos = 0; 19162306a36Sopenharmony_ci u64 config; 19262306a36Sopenharmony_ci char c; 19362306a36Sopenharmony_ci int dirfd; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci dirfd = perf_pmu__event_source_devices_fd(); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci pos += scnprintf(buf + pos, sizeof(buf) - pos, "tsc"); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "caps/mtc", "%d", 20062306a36Sopenharmony_ci &mtc) != 1) 20162306a36Sopenharmony_ci mtc = 1; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (mtc) { 20462306a36Sopenharmony_ci if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "caps/mtc_periods", "%x", 20562306a36Sopenharmony_ci &mtc_periods) != 1) 20662306a36Sopenharmony_ci mtc_periods = 0; 20762306a36Sopenharmony_ci if (mtc_periods) { 20862306a36Sopenharmony_ci mtc_period = intel_pt_pick_bit(mtc_periods, 3); 20962306a36Sopenharmony_ci pos += scnprintf(buf + pos, sizeof(buf) - pos, 21062306a36Sopenharmony_ci ",mtc,mtc_period=%d", mtc_period); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "caps/psb_cyc", "%d", 21562306a36Sopenharmony_ci &psb_cyc) != 1) 21662306a36Sopenharmony_ci psb_cyc = 1; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (psb_cyc && mtc_periods) { 21962306a36Sopenharmony_ci if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "caps/psb_periods", "%x", 22062306a36Sopenharmony_ci &psb_periods) != 1) 22162306a36Sopenharmony_ci psb_periods = 0; 22262306a36Sopenharmony_ci if (psb_periods) { 22362306a36Sopenharmony_ci psb_period = intel_pt_pick_bit(psb_periods, 3); 22462306a36Sopenharmony_ci pos += scnprintf(buf + pos, sizeof(buf) - pos, 22562306a36Sopenharmony_ci ",psb_period=%d", psb_period); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "format/pt", "%c", &c) == 1 && 23062306a36Sopenharmony_ci perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "format/branch", "%c", &c) == 1) 23162306a36Sopenharmony_ci pos += scnprintf(buf + pos, sizeof(buf) - pos, ",pt,branch"); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci pr_debug2("%s default config: %s\n", intel_pt_pmu->name, buf); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci intel_pt_parse_terms(intel_pt_pmu, buf, &config); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci close(dirfd); 23862306a36Sopenharmony_ci return config; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int intel_pt_parse_snapshot_options(struct auxtrace_record *itr, 24262306a36Sopenharmony_ci struct record_opts *opts, 24362306a36Sopenharmony_ci const char *str) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct intel_pt_recording *ptr = 24662306a36Sopenharmony_ci container_of(itr, struct intel_pt_recording, itr); 24762306a36Sopenharmony_ci unsigned long long snapshot_size = 0; 24862306a36Sopenharmony_ci char *endptr; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (str) { 25162306a36Sopenharmony_ci snapshot_size = strtoull(str, &endptr, 0); 25262306a36Sopenharmony_ci if (*endptr || snapshot_size > SIZE_MAX) 25362306a36Sopenharmony_ci return -1; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci opts->auxtrace_snapshot_mode = true; 25762306a36Sopenharmony_ci opts->auxtrace_snapshot_size = snapshot_size; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ptr->snapshot_size = snapshot_size; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistruct perf_event_attr * 26562306a36Sopenharmony_ciintel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct perf_event_attr *attr; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci attr = zalloc(sizeof(struct perf_event_attr)); 27062306a36Sopenharmony_ci if (!attr) 27162306a36Sopenharmony_ci return NULL; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci attr->config = intel_pt_default_config(intel_pt_pmu); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci intel_pt_pmu->selectable = true; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return attr; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic const char *intel_pt_find_filter(struct evlist *evlist, 28162306a36Sopenharmony_ci struct perf_pmu *intel_pt_pmu) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct evsel *evsel; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 28662306a36Sopenharmony_ci if (evsel->core.attr.type == intel_pt_pmu->type) 28762306a36Sopenharmony_ci return evsel->filter; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return NULL; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic size_t intel_pt_filter_bytes(const char *filter) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci size_t len = filter ? strlen(filter) : 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return len ? roundup(len + 1, 8) : 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic size_t 30162306a36Sopenharmony_ciintel_pt_info_priv_size(struct auxtrace_record *itr, struct evlist *evlist) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct intel_pt_recording *ptr = 30462306a36Sopenharmony_ci container_of(itr, struct intel_pt_recording, itr); 30562306a36Sopenharmony_ci const char *filter = intel_pt_find_filter(evlist, ptr->intel_pt_pmu); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci ptr->priv_size = (INTEL_PT_AUXTRACE_PRIV_MAX * sizeof(u64)) + 30862306a36Sopenharmony_ci intel_pt_filter_bytes(filter); 30962306a36Sopenharmony_ci ptr->priv_size += sizeof(u64); /* Cap Event Trace */ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return ptr->priv_size; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void intel_pt_tsc_ctc_ratio(u32 *n, u32 *d) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci __get_cpuid(0x15, &eax, &ebx, &ecx, &edx); 31962306a36Sopenharmony_ci *n = ebx; 32062306a36Sopenharmony_ci *d = eax; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int intel_pt_info_fill(struct auxtrace_record *itr, 32462306a36Sopenharmony_ci struct perf_session *session, 32562306a36Sopenharmony_ci struct perf_record_auxtrace_info *auxtrace_info, 32662306a36Sopenharmony_ci size_t priv_size) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct intel_pt_recording *ptr = 32962306a36Sopenharmony_ci container_of(itr, struct intel_pt_recording, itr); 33062306a36Sopenharmony_ci struct perf_pmu *intel_pt_pmu = ptr->intel_pt_pmu; 33162306a36Sopenharmony_ci struct perf_event_mmap_page *pc; 33262306a36Sopenharmony_ci struct perf_tsc_conversion tc = { .time_mult = 0, }; 33362306a36Sopenharmony_ci bool cap_user_time_zero = false, per_cpu_mmaps; 33462306a36Sopenharmony_ci u64 tsc_bit, mtc_bit, mtc_freq_bits, cyc_bit, noretcomp_bit; 33562306a36Sopenharmony_ci u32 tsc_ctc_ratio_n, tsc_ctc_ratio_d; 33662306a36Sopenharmony_ci unsigned long max_non_turbo_ratio; 33762306a36Sopenharmony_ci size_t filter_str_len; 33862306a36Sopenharmony_ci const char *filter; 33962306a36Sopenharmony_ci int event_trace; 34062306a36Sopenharmony_ci __u64 *info; 34162306a36Sopenharmony_ci int err; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (priv_size != ptr->priv_size) 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci intel_pt_parse_terms(intel_pt_pmu, "tsc", &tsc_bit); 34762306a36Sopenharmony_ci intel_pt_parse_terms(intel_pt_pmu, "noretcomp", &noretcomp_bit); 34862306a36Sopenharmony_ci intel_pt_parse_terms(intel_pt_pmu, "mtc", &mtc_bit); 34962306a36Sopenharmony_ci mtc_freq_bits = perf_pmu__format_bits(intel_pt_pmu, "mtc_period"); 35062306a36Sopenharmony_ci intel_pt_parse_terms(intel_pt_pmu, "cyc", &cyc_bit); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci intel_pt_tsc_ctc_ratio(&tsc_ctc_ratio_n, &tsc_ctc_ratio_d); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (perf_pmu__scan_file(intel_pt_pmu, "max_nonturbo_ratio", 35562306a36Sopenharmony_ci "%lu", &max_non_turbo_ratio) != 1) 35662306a36Sopenharmony_ci max_non_turbo_ratio = 0; 35762306a36Sopenharmony_ci if (perf_pmu__scan_file(intel_pt_pmu, "caps/event_trace", 35862306a36Sopenharmony_ci "%d", &event_trace) != 1) 35962306a36Sopenharmony_ci event_trace = 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci filter = intel_pt_find_filter(session->evlist, ptr->intel_pt_pmu); 36262306a36Sopenharmony_ci filter_str_len = filter ? strlen(filter) : 0; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (!session->evlist->core.nr_mmaps) 36562306a36Sopenharmony_ci return -EINVAL; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci pc = session->evlist->mmap[0].core.base; 36862306a36Sopenharmony_ci if (pc) { 36962306a36Sopenharmony_ci err = perf_read_tsc_conversion(pc, &tc); 37062306a36Sopenharmony_ci if (err) { 37162306a36Sopenharmony_ci if (err != -EOPNOTSUPP) 37262306a36Sopenharmony_ci return err; 37362306a36Sopenharmony_ci } else { 37462306a36Sopenharmony_ci cap_user_time_zero = tc.time_mult != 0; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci if (!cap_user_time_zero) 37762306a36Sopenharmony_ci ui__warning("Intel Processor Trace: TSC not available\n"); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci per_cpu_mmaps = !perf_cpu_map__empty(session->evlist->core.user_requested_cpus); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci auxtrace_info->type = PERF_AUXTRACE_INTEL_PT; 38362306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_PMU_TYPE] = intel_pt_pmu->type; 38462306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_TIME_SHIFT] = tc.time_shift; 38562306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_TIME_MULT] = tc.time_mult; 38662306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_TIME_ZERO] = tc.time_zero; 38762306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_CAP_USER_TIME_ZERO] = cap_user_time_zero; 38862306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_TSC_BIT] = tsc_bit; 38962306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_NORETCOMP_BIT] = noretcomp_bit; 39062306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_HAVE_SCHED_SWITCH] = ptr->have_sched_switch; 39162306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_SNAPSHOT_MODE] = ptr->snapshot_mode; 39262306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_PER_CPU_MMAPS] = per_cpu_mmaps; 39362306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_MTC_BIT] = mtc_bit; 39462306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_MTC_FREQ_BITS] = mtc_freq_bits; 39562306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_TSC_CTC_N] = tsc_ctc_ratio_n; 39662306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_TSC_CTC_D] = tsc_ctc_ratio_d; 39762306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_CYC_BIT] = cyc_bit; 39862306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_MAX_NONTURBO_RATIO] = max_non_turbo_ratio; 39962306a36Sopenharmony_ci auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] = filter_str_len; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci info = &auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] + 1; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (filter_str_len) { 40462306a36Sopenharmony_ci size_t len = intel_pt_filter_bytes(filter); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci strncpy((char *)info, filter, len); 40762306a36Sopenharmony_ci info += len >> 3; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci *info++ = event_trace; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci#ifdef HAVE_LIBTRACEEVENT 41662306a36Sopenharmony_cistatic int intel_pt_track_switches(struct evlist *evlist) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci const char *sched_switch = "sched:sched_switch"; 41962306a36Sopenharmony_ci struct evsel *evsel; 42062306a36Sopenharmony_ci int err; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (!evlist__can_select_event(evlist, sched_switch)) 42362306a36Sopenharmony_ci return -EPERM; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci evsel = evlist__add_sched_switch(evlist, true); 42662306a36Sopenharmony_ci if (IS_ERR(evsel)) { 42762306a36Sopenharmony_ci err = PTR_ERR(evsel); 42862306a36Sopenharmony_ci pr_debug2("%s: failed to create %s, error = %d\n", 42962306a36Sopenharmony_ci __func__, sched_switch, err); 43062306a36Sopenharmony_ci return err; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci evsel->immediate = true; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci#endif 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic void intel_pt_valid_str(char *str, size_t len, u64 valid) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci unsigned int val, last = 0, state = 1; 44262306a36Sopenharmony_ci int p = 0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci str[0] = '\0'; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci for (val = 0; val <= 64; val++, valid >>= 1) { 44762306a36Sopenharmony_ci if (valid & 1) { 44862306a36Sopenharmony_ci last = val; 44962306a36Sopenharmony_ci switch (state) { 45062306a36Sopenharmony_ci case 0: 45162306a36Sopenharmony_ci p += scnprintf(str + p, len - p, ","); 45262306a36Sopenharmony_ci /* Fall through */ 45362306a36Sopenharmony_ci case 1: 45462306a36Sopenharmony_ci p += scnprintf(str + p, len - p, "%u", val); 45562306a36Sopenharmony_ci state = 2; 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci case 2: 45862306a36Sopenharmony_ci state = 3; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case 3: 46162306a36Sopenharmony_ci state = 4; 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci default: 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci } else { 46762306a36Sopenharmony_ci switch (state) { 46862306a36Sopenharmony_ci case 3: 46962306a36Sopenharmony_ci p += scnprintf(str + p, len - p, ",%u", last); 47062306a36Sopenharmony_ci state = 0; 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci case 4: 47362306a36Sopenharmony_ci p += scnprintf(str + p, len - p, "-%u", last); 47462306a36Sopenharmony_ci state = 0; 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci default: 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci if (state != 1) 48062306a36Sopenharmony_ci state = 0; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int intel_pt_val_config_term(struct perf_pmu *intel_pt_pmu, int dirfd, 48662306a36Sopenharmony_ci const char *caps, const char *name, 48762306a36Sopenharmony_ci const char *supported, u64 config) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci char valid_str[256]; 49062306a36Sopenharmony_ci unsigned int shift; 49162306a36Sopenharmony_ci unsigned long long valid; 49262306a36Sopenharmony_ci u64 bits; 49362306a36Sopenharmony_ci int ok; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, caps, "%llx", &valid) != 1) 49662306a36Sopenharmony_ci valid = 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (supported && 49962306a36Sopenharmony_ci perf_pmu__scan_file_at(intel_pt_pmu, dirfd, supported, "%d", &ok) == 1 && !ok) 50062306a36Sopenharmony_ci valid = 0; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci valid |= 1; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci bits = perf_pmu__format_bits(intel_pt_pmu, name); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci config &= bits; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci for (shift = 0; bits && !(bits & 1); shift++) 50962306a36Sopenharmony_ci bits >>= 1; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci config >>= shift; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (config > 63) 51462306a36Sopenharmony_ci goto out_err; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (valid & (1 << config)) 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ciout_err: 51962306a36Sopenharmony_ci intel_pt_valid_str(valid_str, sizeof(valid_str), valid); 52062306a36Sopenharmony_ci pr_err("Invalid %s for %s. Valid values are: %s\n", 52162306a36Sopenharmony_ci name, INTEL_PT_PMU_NAME, valid_str); 52262306a36Sopenharmony_ci return -EINVAL; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int intel_pt_validate_config(struct perf_pmu *intel_pt_pmu, 52662306a36Sopenharmony_ci struct evsel *evsel) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci int err, dirfd; 52962306a36Sopenharmony_ci char c; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!evsel) 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci dirfd = perf_pmu__event_source_devices_fd(); 53562306a36Sopenharmony_ci if (dirfd < 0) 53662306a36Sopenharmony_ci return dirfd; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* 53962306a36Sopenharmony_ci * If supported, force pass-through config term (pt=1) even if user 54062306a36Sopenharmony_ci * sets pt=0, which avoids senseless kernel errors. 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci if (perf_pmu__scan_file_at(intel_pt_pmu, dirfd, "format/pt", "%c", &c) == 1 && 54362306a36Sopenharmony_ci !(evsel->core.attr.config & 1)) { 54462306a36Sopenharmony_ci pr_warning("pt=0 doesn't make sense, forcing pt=1\n"); 54562306a36Sopenharmony_ci evsel->core.attr.config |= 1; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci err = intel_pt_val_config_term(intel_pt_pmu, dirfd, "caps/cycle_thresholds", 54962306a36Sopenharmony_ci "cyc_thresh", "caps/psb_cyc", 55062306a36Sopenharmony_ci evsel->core.attr.config); 55162306a36Sopenharmony_ci if (err) 55262306a36Sopenharmony_ci goto out; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci err = intel_pt_val_config_term(intel_pt_pmu, dirfd, "caps/mtc_periods", 55562306a36Sopenharmony_ci "mtc_period", "caps/mtc", 55662306a36Sopenharmony_ci evsel->core.attr.config); 55762306a36Sopenharmony_ci if (err) 55862306a36Sopenharmony_ci goto out; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci err = intel_pt_val_config_term(intel_pt_pmu, dirfd, "caps/psb_periods", 56162306a36Sopenharmony_ci "psb_period", "caps/psb_cyc", 56262306a36Sopenharmony_ci evsel->core.attr.config); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ciout: 56562306a36Sopenharmony_ci close(dirfd); 56662306a36Sopenharmony_ci return err; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic void intel_pt_min_max_sample_sz(struct evlist *evlist, 57062306a36Sopenharmony_ci size_t *min_sz, size_t *max_sz) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct evsel *evsel; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 57562306a36Sopenharmony_ci size_t sz = evsel->core.attr.aux_sample_size; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!sz) 57862306a36Sopenharmony_ci continue; 57962306a36Sopenharmony_ci if (min_sz && (sz < *min_sz || !*min_sz)) 58062306a36Sopenharmony_ci *min_sz = sz; 58162306a36Sopenharmony_ci if (max_sz && sz > *max_sz) 58262306a36Sopenharmony_ci *max_sz = sz; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci/* 58762306a36Sopenharmony_ci * Currently, there is not enough information to disambiguate different PEBS 58862306a36Sopenharmony_ci * events, so only allow one. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_cistatic bool intel_pt_too_many_aux_output(struct evlist *evlist) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct evsel *evsel; 59362306a36Sopenharmony_ci int aux_output_cnt = 0; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci evlist__for_each_entry(evlist, evsel) 59662306a36Sopenharmony_ci aux_output_cnt += !!evsel->core.attr.aux_output; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (aux_output_cnt > 1) { 59962306a36Sopenharmony_ci pr_err(INTEL_PT_PMU_NAME " supports at most one event with aux-output\n"); 60062306a36Sopenharmony_ci return true; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return false; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int intel_pt_recording_options(struct auxtrace_record *itr, 60762306a36Sopenharmony_ci struct evlist *evlist, 60862306a36Sopenharmony_ci struct record_opts *opts) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct intel_pt_recording *ptr = 61162306a36Sopenharmony_ci container_of(itr, struct intel_pt_recording, itr); 61262306a36Sopenharmony_ci struct perf_pmu *intel_pt_pmu = ptr->intel_pt_pmu; 61362306a36Sopenharmony_ci bool have_timing_info, need_immediate = false; 61462306a36Sopenharmony_ci struct evsel *evsel, *intel_pt_evsel = NULL; 61562306a36Sopenharmony_ci const struct perf_cpu_map *cpus = evlist->core.user_requested_cpus; 61662306a36Sopenharmony_ci bool privileged = perf_event_paranoid_check(-1); 61762306a36Sopenharmony_ci u64 tsc_bit; 61862306a36Sopenharmony_ci int err; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ptr->evlist = evlist; 62162306a36Sopenharmony_ci ptr->snapshot_mode = opts->auxtrace_snapshot_mode; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 62462306a36Sopenharmony_ci if (evsel->core.attr.type == intel_pt_pmu->type) { 62562306a36Sopenharmony_ci if (intel_pt_evsel) { 62662306a36Sopenharmony_ci pr_err("There may be only one " INTEL_PT_PMU_NAME " event\n"); 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci evsel->core.attr.freq = 0; 63062306a36Sopenharmony_ci evsel->core.attr.sample_period = 1; 63162306a36Sopenharmony_ci evsel->no_aux_samples = true; 63262306a36Sopenharmony_ci evsel->needs_auxtrace_mmap = true; 63362306a36Sopenharmony_ci intel_pt_evsel = evsel; 63462306a36Sopenharmony_ci opts->full_auxtrace = true; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (opts->auxtrace_snapshot_mode && !opts->full_auxtrace) { 63962306a36Sopenharmony_ci pr_err("Snapshot mode (-S option) requires " INTEL_PT_PMU_NAME " PMU event (-e " INTEL_PT_PMU_NAME ")\n"); 64062306a36Sopenharmony_ci return -EINVAL; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (opts->auxtrace_snapshot_mode && opts->auxtrace_sample_mode) { 64462306a36Sopenharmony_ci pr_err("Snapshot mode (" INTEL_PT_PMU_NAME " PMU) and sample trace cannot be used together\n"); 64562306a36Sopenharmony_ci return -EINVAL; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (opts->use_clockid) { 64962306a36Sopenharmony_ci pr_err("Cannot use clockid (-k option) with " INTEL_PT_PMU_NAME "\n"); 65062306a36Sopenharmony_ci return -EINVAL; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (intel_pt_too_many_aux_output(evlist)) 65462306a36Sopenharmony_ci return -EINVAL; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!opts->full_auxtrace) 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (opts->auxtrace_sample_mode) 66062306a36Sopenharmony_ci evsel__set_config_if_unset(intel_pt_pmu, intel_pt_evsel, 66162306a36Sopenharmony_ci "psb_period", 0); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci err = intel_pt_validate_config(intel_pt_pmu, intel_pt_evsel); 66462306a36Sopenharmony_ci if (err) 66562306a36Sopenharmony_ci return err; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* Set default sizes for snapshot mode */ 66862306a36Sopenharmony_ci if (opts->auxtrace_snapshot_mode) { 66962306a36Sopenharmony_ci size_t psb_period = intel_pt_psb_period(intel_pt_pmu, evlist); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) { 67262306a36Sopenharmony_ci if (privileged) { 67362306a36Sopenharmony_ci opts->auxtrace_mmap_pages = MiB(4) / page_size; 67462306a36Sopenharmony_ci } else { 67562306a36Sopenharmony_ci opts->auxtrace_mmap_pages = KiB(128) / page_size; 67662306a36Sopenharmony_ci if (opts->mmap_pages == UINT_MAX) 67762306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci } else if (!opts->auxtrace_mmap_pages && !privileged && 68062306a36Sopenharmony_ci opts->mmap_pages == UINT_MAX) { 68162306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size) 68462306a36Sopenharmony_ci opts->auxtrace_snapshot_size = 68562306a36Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size; 68662306a36Sopenharmony_ci if (!opts->auxtrace_mmap_pages) { 68762306a36Sopenharmony_ci size_t sz = opts->auxtrace_snapshot_size; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci sz = round_up(sz, page_size) / page_size; 69062306a36Sopenharmony_ci opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci if (opts->auxtrace_snapshot_size > 69362306a36Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size) { 69462306a36Sopenharmony_ci pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n", 69562306a36Sopenharmony_ci opts->auxtrace_snapshot_size, 69662306a36Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size); 69762306a36Sopenharmony_ci return -EINVAL; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) { 70062306a36Sopenharmony_ci pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n"); 70162306a36Sopenharmony_ci return -EINVAL; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci pr_debug2("Intel PT snapshot size: %zu\n", 70462306a36Sopenharmony_ci opts->auxtrace_snapshot_size); 70562306a36Sopenharmony_ci if (psb_period && 70662306a36Sopenharmony_ci opts->auxtrace_snapshot_size <= psb_period + 70762306a36Sopenharmony_ci INTEL_PT_PSB_PERIOD_NEAR) 70862306a36Sopenharmony_ci ui__warning("Intel PT snapshot size (%zu) may be too small for PSB period (%zu)\n", 70962306a36Sopenharmony_ci opts->auxtrace_snapshot_size, psb_period); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* Set default sizes for sample mode */ 71362306a36Sopenharmony_ci if (opts->auxtrace_sample_mode) { 71462306a36Sopenharmony_ci size_t psb_period = intel_pt_psb_period(intel_pt_pmu, evlist); 71562306a36Sopenharmony_ci size_t min_sz = 0, max_sz = 0; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci intel_pt_min_max_sample_sz(evlist, &min_sz, &max_sz); 71862306a36Sopenharmony_ci if (!opts->auxtrace_mmap_pages && !privileged && 71962306a36Sopenharmony_ci opts->mmap_pages == UINT_MAX) 72062306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 72162306a36Sopenharmony_ci if (!opts->auxtrace_mmap_pages) { 72262306a36Sopenharmony_ci size_t sz = round_up(max_sz, page_size) / page_size; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci if (max_sz > opts->auxtrace_mmap_pages * (size_t)page_size) { 72762306a36Sopenharmony_ci pr_err("Sample size %zu must not be greater than AUX area tracing mmap size %zu\n", 72862306a36Sopenharmony_ci max_sz, 72962306a36Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size); 73062306a36Sopenharmony_ci return -EINVAL; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci pr_debug2("Intel PT min. sample size: %zu max. sample size: %zu\n", 73362306a36Sopenharmony_ci min_sz, max_sz); 73462306a36Sopenharmony_ci if (psb_period && 73562306a36Sopenharmony_ci min_sz <= psb_period + INTEL_PT_PSB_PERIOD_NEAR) 73662306a36Sopenharmony_ci ui__warning("Intel PT sample size (%zu) may be too small for PSB period (%zu)\n", 73762306a36Sopenharmony_ci min_sz, psb_period); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* Set default sizes for full trace mode */ 74162306a36Sopenharmony_ci if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) { 74262306a36Sopenharmony_ci if (privileged) { 74362306a36Sopenharmony_ci opts->auxtrace_mmap_pages = MiB(4) / page_size; 74462306a36Sopenharmony_ci } else { 74562306a36Sopenharmony_ci opts->auxtrace_mmap_pages = KiB(128) / page_size; 74662306a36Sopenharmony_ci if (opts->mmap_pages == UINT_MAX) 74762306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* Validate auxtrace_mmap_pages */ 75262306a36Sopenharmony_ci if (opts->auxtrace_mmap_pages) { 75362306a36Sopenharmony_ci size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; 75462306a36Sopenharmony_ci size_t min_sz; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (opts->auxtrace_snapshot_mode || opts->auxtrace_sample_mode) 75762306a36Sopenharmony_ci min_sz = KiB(4); 75862306a36Sopenharmony_ci else 75962306a36Sopenharmony_ci min_sz = KiB(8); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (sz < min_sz || !is_power_of_2(sz)) { 76262306a36Sopenharmony_ci pr_err("Invalid mmap size for Intel Processor Trace: must be at least %zuKiB and a power of 2\n", 76362306a36Sopenharmony_ci min_sz / 1024); 76462306a36Sopenharmony_ci return -EINVAL; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_mode && !opts->auxtrace_sample_mode) { 76962306a36Sopenharmony_ci u32 aux_watermark = opts->auxtrace_mmap_pages * page_size / 4; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci intel_pt_evsel->core.attr.aux_watermark = aux_watermark; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci intel_pt_parse_terms(intel_pt_pmu, "tsc", &tsc_bit); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (opts->full_auxtrace && (intel_pt_evsel->core.attr.config & tsc_bit)) 77762306a36Sopenharmony_ci have_timing_info = true; 77862306a36Sopenharmony_ci else 77962306a36Sopenharmony_ci have_timing_info = false; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* 78262306a36Sopenharmony_ci * Per-cpu recording needs sched_switch events to distinguish different 78362306a36Sopenharmony_ci * threads. 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci if (have_timing_info && !perf_cpu_map__empty(cpus) && 78662306a36Sopenharmony_ci !record_opts__no_switch_events(opts)) { 78762306a36Sopenharmony_ci if (perf_can_record_switch_events()) { 78862306a36Sopenharmony_ci bool cpu_wide = !target__none(&opts->target) && 78962306a36Sopenharmony_ci !target__has_task(&opts->target); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (!cpu_wide && perf_can_record_cpu_wide()) { 79262306a36Sopenharmony_ci struct evsel *switch_evsel; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci switch_evsel = evlist__add_dummy_on_all_cpus(evlist); 79562306a36Sopenharmony_ci if (!switch_evsel) 79662306a36Sopenharmony_ci return -ENOMEM; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci switch_evsel->core.attr.context_switch = 1; 79962306a36Sopenharmony_ci switch_evsel->immediate = true; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci evsel__set_sample_bit(switch_evsel, TID); 80262306a36Sopenharmony_ci evsel__set_sample_bit(switch_evsel, TIME); 80362306a36Sopenharmony_ci evsel__set_sample_bit(switch_evsel, CPU); 80462306a36Sopenharmony_ci evsel__reset_sample_bit(switch_evsel, BRANCH_STACK); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci opts->record_switch_events = false; 80762306a36Sopenharmony_ci ptr->have_sched_switch = 3; 80862306a36Sopenharmony_ci } else { 80962306a36Sopenharmony_ci opts->record_switch_events = true; 81062306a36Sopenharmony_ci need_immediate = true; 81162306a36Sopenharmony_ci if (cpu_wide) 81262306a36Sopenharmony_ci ptr->have_sched_switch = 3; 81362306a36Sopenharmony_ci else 81462306a36Sopenharmony_ci ptr->have_sched_switch = 2; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci } else { 81762306a36Sopenharmony_ci#ifdef HAVE_LIBTRACEEVENT 81862306a36Sopenharmony_ci err = intel_pt_track_switches(evlist); 81962306a36Sopenharmony_ci if (err == -EPERM) 82062306a36Sopenharmony_ci pr_debug2("Unable to select sched:sched_switch\n"); 82162306a36Sopenharmony_ci else if (err) 82262306a36Sopenharmony_ci return err; 82362306a36Sopenharmony_ci else 82462306a36Sopenharmony_ci ptr->have_sched_switch = 1; 82562306a36Sopenharmony_ci#endif 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (have_timing_info && !intel_pt_evsel->core.attr.exclude_kernel && 83062306a36Sopenharmony_ci perf_can_record_text_poke_events() && perf_can_record_cpu_wide()) 83162306a36Sopenharmony_ci opts->text_poke = true; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (intel_pt_evsel) { 83462306a36Sopenharmony_ci /* 83562306a36Sopenharmony_ci * To obtain the auxtrace buffer file descriptor, the auxtrace 83662306a36Sopenharmony_ci * event must come first. 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci evlist__to_front(evlist, intel_pt_evsel); 83962306a36Sopenharmony_ci /* 84062306a36Sopenharmony_ci * In the case of per-cpu mmaps, we need the CPU on the 84162306a36Sopenharmony_ci * AUX event. 84262306a36Sopenharmony_ci */ 84362306a36Sopenharmony_ci if (!perf_cpu_map__empty(cpus)) 84462306a36Sopenharmony_ci evsel__set_sample_bit(intel_pt_evsel, CPU); 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* Add dummy event to keep tracking */ 84862306a36Sopenharmony_ci if (opts->full_auxtrace) { 84962306a36Sopenharmony_ci bool need_system_wide_tracking; 85062306a36Sopenharmony_ci struct evsel *tracking_evsel; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* 85362306a36Sopenharmony_ci * User space tasks can migrate between CPUs, so when tracing 85462306a36Sopenharmony_ci * selected CPUs, sideband for all CPUs is still needed. 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci need_system_wide_tracking = opts->target.cpu_list && 85762306a36Sopenharmony_ci !intel_pt_evsel->core.attr.exclude_user; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci tracking_evsel = evlist__add_aux_dummy(evlist, need_system_wide_tracking); 86062306a36Sopenharmony_ci if (!tracking_evsel) 86162306a36Sopenharmony_ci return -ENOMEM; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci evlist__set_tracking_event(evlist, tracking_evsel); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (need_immediate) 86662306a36Sopenharmony_ci tracking_evsel->immediate = true; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* In per-cpu case, always need the time of mmap events etc */ 86962306a36Sopenharmony_ci if (!perf_cpu_map__empty(cpus)) { 87062306a36Sopenharmony_ci evsel__set_sample_bit(tracking_evsel, TIME); 87162306a36Sopenharmony_ci /* And the CPU for switch events */ 87262306a36Sopenharmony_ci evsel__set_sample_bit(tracking_evsel, CPU); 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* 87862306a36Sopenharmony_ci * Warn the user when we do not have enough information to decode i.e. 87962306a36Sopenharmony_ci * per-cpu with no sched_switch (except workload-only). 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_ci if (!ptr->have_sched_switch && !perf_cpu_map__empty(cpus) && 88262306a36Sopenharmony_ci !target__none(&opts->target) && 88362306a36Sopenharmony_ci !intel_pt_evsel->core.attr.exclude_user) 88462306a36Sopenharmony_ci ui__warning("Intel Processor Trace decoding will not be possible except for kernel tracing!\n"); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return 0; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic int intel_pt_snapshot_start(struct auxtrace_record *itr) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci struct intel_pt_recording *ptr = 89262306a36Sopenharmony_ci container_of(itr, struct intel_pt_recording, itr); 89362306a36Sopenharmony_ci struct evsel *evsel; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci evlist__for_each_entry(ptr->evlist, evsel) { 89662306a36Sopenharmony_ci if (evsel->core.attr.type == ptr->intel_pt_pmu->type) 89762306a36Sopenharmony_ci return evsel__disable(evsel); 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci return -EINVAL; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic int intel_pt_snapshot_finish(struct auxtrace_record *itr) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci struct intel_pt_recording *ptr = 90562306a36Sopenharmony_ci container_of(itr, struct intel_pt_recording, itr); 90662306a36Sopenharmony_ci struct evsel *evsel; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci evlist__for_each_entry(ptr->evlist, evsel) { 90962306a36Sopenharmony_ci if (evsel->core.attr.type == ptr->intel_pt_pmu->type) 91062306a36Sopenharmony_ci return evsel__enable(evsel); 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci return -EINVAL; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic int intel_pt_alloc_snapshot_refs(struct intel_pt_recording *ptr, int idx) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci const size_t sz = sizeof(struct intel_pt_snapshot_ref); 91862306a36Sopenharmony_ci int cnt = ptr->snapshot_ref_cnt, new_cnt = cnt * 2; 91962306a36Sopenharmony_ci struct intel_pt_snapshot_ref *refs; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (!new_cnt) 92262306a36Sopenharmony_ci new_cnt = 16; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci while (new_cnt <= idx) 92562306a36Sopenharmony_ci new_cnt *= 2; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci refs = calloc(new_cnt, sz); 92862306a36Sopenharmony_ci if (!refs) 92962306a36Sopenharmony_ci return -ENOMEM; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci memcpy(refs, ptr->snapshot_refs, cnt * sz); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci ptr->snapshot_refs = refs; 93462306a36Sopenharmony_ci ptr->snapshot_ref_cnt = new_cnt; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return 0; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic void intel_pt_free_snapshot_refs(struct intel_pt_recording *ptr) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci int i; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci for (i = 0; i < ptr->snapshot_ref_cnt; i++) 94462306a36Sopenharmony_ci zfree(&ptr->snapshot_refs[i].ref_buf); 94562306a36Sopenharmony_ci zfree(&ptr->snapshot_refs); 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic void intel_pt_recording_free(struct auxtrace_record *itr) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct intel_pt_recording *ptr = 95162306a36Sopenharmony_ci container_of(itr, struct intel_pt_recording, itr); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci intel_pt_free_snapshot_refs(ptr); 95462306a36Sopenharmony_ci free(ptr); 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic int intel_pt_alloc_snapshot_ref(struct intel_pt_recording *ptr, int idx, 95862306a36Sopenharmony_ci size_t snapshot_buf_size) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci size_t ref_buf_size = ptr->snapshot_ref_buf_size; 96162306a36Sopenharmony_ci void *ref_buf; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci ref_buf = zalloc(ref_buf_size); 96462306a36Sopenharmony_ci if (!ref_buf) 96562306a36Sopenharmony_ci return -ENOMEM; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci ptr->snapshot_refs[idx].ref_buf = ref_buf; 96862306a36Sopenharmony_ci ptr->snapshot_refs[idx].ref_offset = snapshot_buf_size - ref_buf_size; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return 0; 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic size_t intel_pt_snapshot_ref_buf_size(struct intel_pt_recording *ptr, 97462306a36Sopenharmony_ci size_t snapshot_buf_size) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci const size_t max_size = 256 * 1024; 97762306a36Sopenharmony_ci size_t buf_size = 0, psb_period; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (ptr->snapshot_size <= 64 * 1024) 98062306a36Sopenharmony_ci return 0; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci psb_period = intel_pt_psb_period(ptr->intel_pt_pmu, ptr->evlist); 98362306a36Sopenharmony_ci if (psb_period) 98462306a36Sopenharmony_ci buf_size = psb_period * 2; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (!buf_size || buf_size > max_size) 98762306a36Sopenharmony_ci buf_size = max_size; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (buf_size >= snapshot_buf_size) 99062306a36Sopenharmony_ci return 0; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (buf_size >= ptr->snapshot_size / 2) 99362306a36Sopenharmony_ci return 0; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return buf_size; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic int intel_pt_snapshot_init(struct intel_pt_recording *ptr, 99962306a36Sopenharmony_ci size_t snapshot_buf_size) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci if (ptr->snapshot_init_done) 100262306a36Sopenharmony_ci return 0; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci ptr->snapshot_init_done = true; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci ptr->snapshot_ref_buf_size = intel_pt_snapshot_ref_buf_size(ptr, 100762306a36Sopenharmony_ci snapshot_buf_size); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci return 0; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci/** 101362306a36Sopenharmony_ci * intel_pt_compare_buffers - compare bytes in a buffer to a circular buffer. 101462306a36Sopenharmony_ci * @buf1: first buffer 101562306a36Sopenharmony_ci * @compare_size: number of bytes to compare 101662306a36Sopenharmony_ci * @buf2: second buffer (a circular buffer) 101762306a36Sopenharmony_ci * @offs2: offset in second buffer 101862306a36Sopenharmony_ci * @buf2_size: size of second buffer 101962306a36Sopenharmony_ci * 102062306a36Sopenharmony_ci * The comparison allows for the possibility that the bytes to compare in the 102162306a36Sopenharmony_ci * circular buffer are not contiguous. It is assumed that @compare_size <= 102262306a36Sopenharmony_ci * @buf2_size. This function returns %false if the bytes are identical, %true 102362306a36Sopenharmony_ci * otherwise. 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_cistatic bool intel_pt_compare_buffers(void *buf1, size_t compare_size, 102662306a36Sopenharmony_ci void *buf2, size_t offs2, size_t buf2_size) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci size_t end2 = offs2 + compare_size, part_size; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (end2 <= buf2_size) 103162306a36Sopenharmony_ci return memcmp(buf1, buf2 + offs2, compare_size); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci part_size = end2 - buf2_size; 103462306a36Sopenharmony_ci if (memcmp(buf1, buf2 + offs2, part_size)) 103562306a36Sopenharmony_ci return true; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci compare_size -= part_size; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci return memcmp(buf1 + part_size, buf2, compare_size); 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic bool intel_pt_compare_ref(void *ref_buf, size_t ref_offset, 104362306a36Sopenharmony_ci size_t ref_size, size_t buf_size, 104462306a36Sopenharmony_ci void *data, size_t head) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci size_t ref_end = ref_offset + ref_size; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (ref_end > buf_size) { 104962306a36Sopenharmony_ci if (head > ref_offset || head < ref_end - buf_size) 105062306a36Sopenharmony_ci return true; 105162306a36Sopenharmony_ci } else if (head > ref_offset && head < ref_end) { 105262306a36Sopenharmony_ci return true; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci return intel_pt_compare_buffers(ref_buf, ref_size, data, ref_offset, 105662306a36Sopenharmony_ci buf_size); 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic void intel_pt_copy_ref(void *ref_buf, size_t ref_size, size_t buf_size, 106062306a36Sopenharmony_ci void *data, size_t head) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci if (head >= ref_size) { 106362306a36Sopenharmony_ci memcpy(ref_buf, data + head - ref_size, ref_size); 106462306a36Sopenharmony_ci } else { 106562306a36Sopenharmony_ci memcpy(ref_buf, data, head); 106662306a36Sopenharmony_ci ref_size -= head; 106762306a36Sopenharmony_ci memcpy(ref_buf + head, data + buf_size - ref_size, ref_size); 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic bool intel_pt_wrapped(struct intel_pt_recording *ptr, int idx, 107262306a36Sopenharmony_ci struct auxtrace_mmap *mm, unsigned char *data, 107362306a36Sopenharmony_ci u64 head) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci struct intel_pt_snapshot_ref *ref = &ptr->snapshot_refs[idx]; 107662306a36Sopenharmony_ci bool wrapped; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci wrapped = intel_pt_compare_ref(ref->ref_buf, ref->ref_offset, 107962306a36Sopenharmony_ci ptr->snapshot_ref_buf_size, mm->len, 108062306a36Sopenharmony_ci data, head); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci intel_pt_copy_ref(ref->ref_buf, ptr->snapshot_ref_buf_size, mm->len, 108362306a36Sopenharmony_ci data, head); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return wrapped; 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic bool intel_pt_first_wrap(u64 *data, size_t buf_size) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci int i, a, b; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci b = buf_size >> 3; 109362306a36Sopenharmony_ci a = b - 512; 109462306a36Sopenharmony_ci if (a < 0) 109562306a36Sopenharmony_ci a = 0; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci for (i = a; i < b; i++) { 109862306a36Sopenharmony_ci if (data[i]) 109962306a36Sopenharmony_ci return true; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci return false; 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic int intel_pt_find_snapshot(struct auxtrace_record *itr, int idx, 110662306a36Sopenharmony_ci struct auxtrace_mmap *mm, unsigned char *data, 110762306a36Sopenharmony_ci u64 *head, u64 *old) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct intel_pt_recording *ptr = 111062306a36Sopenharmony_ci container_of(itr, struct intel_pt_recording, itr); 111162306a36Sopenharmony_ci bool wrapped; 111262306a36Sopenharmony_ci int err; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci pr_debug3("%s: mmap index %d old head %zu new head %zu\n", 111562306a36Sopenharmony_ci __func__, idx, (size_t)*old, (size_t)*head); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci err = intel_pt_snapshot_init(ptr, mm->len); 111862306a36Sopenharmony_ci if (err) 111962306a36Sopenharmony_ci goto out_err; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (idx >= ptr->snapshot_ref_cnt) { 112262306a36Sopenharmony_ci err = intel_pt_alloc_snapshot_refs(ptr, idx); 112362306a36Sopenharmony_ci if (err) 112462306a36Sopenharmony_ci goto out_err; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (ptr->snapshot_ref_buf_size) { 112862306a36Sopenharmony_ci if (!ptr->snapshot_refs[idx].ref_buf) { 112962306a36Sopenharmony_ci err = intel_pt_alloc_snapshot_ref(ptr, idx, mm->len); 113062306a36Sopenharmony_ci if (err) 113162306a36Sopenharmony_ci goto out_err; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci wrapped = intel_pt_wrapped(ptr, idx, mm, data, *head); 113462306a36Sopenharmony_ci } else { 113562306a36Sopenharmony_ci wrapped = ptr->snapshot_refs[idx].wrapped; 113662306a36Sopenharmony_ci if (!wrapped && intel_pt_first_wrap((u64 *)data, mm->len)) { 113762306a36Sopenharmony_ci ptr->snapshot_refs[idx].wrapped = true; 113862306a36Sopenharmony_ci wrapped = true; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* 114362306a36Sopenharmony_ci * In full trace mode 'head' continually increases. However in snapshot 114462306a36Sopenharmony_ci * mode 'head' is an offset within the buffer. Here 'old' and 'head' 114562306a36Sopenharmony_ci * are adjusted to match the full trace case which expects that 'old' is 114662306a36Sopenharmony_ci * always less than 'head'. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci if (wrapped) { 114962306a36Sopenharmony_ci *old = *head; 115062306a36Sopenharmony_ci *head += mm->len; 115162306a36Sopenharmony_ci } else { 115262306a36Sopenharmony_ci if (mm->mask) 115362306a36Sopenharmony_ci *old &= mm->mask; 115462306a36Sopenharmony_ci else 115562306a36Sopenharmony_ci *old %= mm->len; 115662306a36Sopenharmony_ci if (*old > *head) 115762306a36Sopenharmony_ci *head += mm->len; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci pr_debug3("%s: wrap-around %sdetected, adjusted old head %zu adjusted new head %zu\n", 116162306a36Sopenharmony_ci __func__, wrapped ? "" : "not ", (size_t)*old, (size_t)*head); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci return 0; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ciout_err: 116662306a36Sopenharmony_ci pr_err("%s: failed, error %d\n", __func__, err); 116762306a36Sopenharmony_ci return err; 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cistatic u64 intel_pt_reference(struct auxtrace_record *itr __maybe_unused) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci return rdtsc(); 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistruct auxtrace_record *intel_pt_recording_init(int *err) 117662306a36Sopenharmony_ci{ 117762306a36Sopenharmony_ci struct perf_pmu *intel_pt_pmu = perf_pmus__find(INTEL_PT_PMU_NAME); 117862306a36Sopenharmony_ci struct intel_pt_recording *ptr; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (!intel_pt_pmu) 118162306a36Sopenharmony_ci return NULL; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) { 118462306a36Sopenharmony_ci *err = -errno; 118562306a36Sopenharmony_ci return NULL; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci ptr = zalloc(sizeof(struct intel_pt_recording)); 118962306a36Sopenharmony_ci if (!ptr) { 119062306a36Sopenharmony_ci *err = -ENOMEM; 119162306a36Sopenharmony_ci return NULL; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci ptr->intel_pt_pmu = intel_pt_pmu; 119562306a36Sopenharmony_ci ptr->itr.pmu = intel_pt_pmu; 119662306a36Sopenharmony_ci ptr->itr.recording_options = intel_pt_recording_options; 119762306a36Sopenharmony_ci ptr->itr.info_priv_size = intel_pt_info_priv_size; 119862306a36Sopenharmony_ci ptr->itr.info_fill = intel_pt_info_fill; 119962306a36Sopenharmony_ci ptr->itr.free = intel_pt_recording_free; 120062306a36Sopenharmony_ci ptr->itr.snapshot_start = intel_pt_snapshot_start; 120162306a36Sopenharmony_ci ptr->itr.snapshot_finish = intel_pt_snapshot_finish; 120262306a36Sopenharmony_ci ptr->itr.find_snapshot = intel_pt_find_snapshot; 120362306a36Sopenharmony_ci ptr->itr.parse_snapshot_options = intel_pt_parse_snapshot_options; 120462306a36Sopenharmony_ci ptr->itr.reference = intel_pt_reference; 120562306a36Sopenharmony_ci ptr->itr.read_finish = auxtrace_record__read_finish; 120662306a36Sopenharmony_ci /* 120762306a36Sopenharmony_ci * Decoding starts at a PSB packet. Minimum PSB period is 2K so 4K 120862306a36Sopenharmony_ci * should give at least 1 PSB per sample. 120962306a36Sopenharmony_ci */ 121062306a36Sopenharmony_ci ptr->itr.default_aux_sample_size = 4096; 121162306a36Sopenharmony_ci return &ptr->itr; 121262306a36Sopenharmony_ci} 1213