diff options
Diffstat (limited to 'tools/perf/arch/arm/util/cs-etm.c')
-rw-r--r-- | tools/perf/arch/arm/util/cs-etm.c | 697 |
1 files changed, 371 insertions, 326 deletions
diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index cea5e33d61d2..77e6663c1703 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -16,19 +16,19 @@ #include <linux/zalloc.h> #include "cs-etm.h" -#include "../../util/debug.h" -#include "../../util/record.h" -#include "../../util/auxtrace.h" -#include "../../util/cpumap.h" -#include "../../util/event.h" -#include "../../util/evlist.h" -#include "../../util/evsel.h" -#include "../../util/perf_api_probe.h" -#include "../../util/evsel_config.h" -#include "../../util/pmu.h" -#include "../../util/cs-etm.h" +#include "../../../util/debug.h" +#include "../../../util/record.h" +#include "../../../util/auxtrace.h" +#include "../../../util/cpumap.h" +#include "../../../util/event.h" +#include "../../../util/evlist.h" +#include "../../../util/evsel.h" +#include "../../../util/perf_api_probe.h" +#include "../../../util/evsel_config.h" +#include "../../../util/pmus.h" +#include "../../../util/cs-etm.h" #include <internal/lib.h> // page_size -#include "../../util/session.h" +#include "../../../util/session.h" #include <errno.h> #include <stdlib.h> @@ -38,8 +38,6 @@ struct cs_etm_recording { struct auxtrace_record itr; struct perf_pmu *cs_etm_pmu; struct evlist *evlist; - int wrapped_cnt; - bool *wrapped; bool snapshot_mode; size_t snapshot_size; }; @@ -49,79 +47,118 @@ static const char *metadata_etmv3_ro[CS_ETM_PRIV_MAX] = { [CS_ETM_ETMIDR] = "mgmt/etmidr", }; -static const char *metadata_etmv4_ro[CS_ETMV4_PRIV_MAX] = { +static const char * const metadata_etmv4_ro[] = { [CS_ETMV4_TRCIDR0] = "trcidr/trcidr0", [CS_ETMV4_TRCIDR1] = "trcidr/trcidr1", [CS_ETMV4_TRCIDR2] = "trcidr/trcidr2", [CS_ETMV4_TRCIDR8] = "trcidr/trcidr8", [CS_ETMV4_TRCAUTHSTATUS] = "mgmt/trcauthstatus", + [CS_ETMV4_TS_SOURCE] = "ts_source", +}; + +static const char * const metadata_ete_ro[] = { + [CS_ETE_TRCIDR0] = "trcidr/trcidr0", + [CS_ETE_TRCIDR1] = "trcidr/trcidr1", + [CS_ETE_TRCIDR2] = "trcidr/trcidr2", + [CS_ETE_TRCIDR8] = "trcidr/trcidr8", + [CS_ETE_TRCAUTHSTATUS] = "mgmt/trcauthstatus", + [CS_ETE_TRCDEVARCH] = "mgmt/trcdevarch", + [CS_ETE_TS_SOURCE] = "ts_source", }; static bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu); +static bool cs_etm_is_ete(struct auxtrace_record *itr, int cpu); -static int cs_etm_set_context_id(struct auxtrace_record *itr, - struct evsel *evsel, int cpu) +static int cs_etm_validate_context_id(struct auxtrace_record *itr, + struct evsel *evsel, int cpu) { - struct cs_etm_recording *ptr; - struct perf_pmu *cs_etm_pmu; + struct cs_etm_recording *ptr = + container_of(itr, struct cs_etm_recording, itr); + struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; char path[PATH_MAX]; - int err = -EINVAL; + int err; u32 val; + u64 contextid = evsel->core.attr.config & + (perf_pmu__format_bits(cs_etm_pmu, "contextid") | + perf_pmu__format_bits(cs_etm_pmu, "contextid1") | + perf_pmu__format_bits(cs_etm_pmu, "contextid2")); - ptr = container_of(itr, struct cs_etm_recording, itr); - cs_etm_pmu = ptr->cs_etm_pmu; + if (!contextid) + return 0; - if (!cs_etm_is_etmv4(itr, cpu)) - goto out; + /* Not supported in etmv3 */ + if (!cs_etm_is_etmv4(itr, cpu)) { + pr_err("%s: contextid not supported in ETMv3, disable with %s/contextid=0/\n", + CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME); + return -EINVAL; + } - /* Get a handle on TRCIRD2 */ + /* Get a handle on TRCIDR2 */ snprintf(path, PATH_MAX, "cpu%d/%s", cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR2]); err = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); /* There was a problem reading the file, bailing out */ if (err != 1) { - pr_err("%s: can't read file %s\n", - CORESIGHT_ETM_PMU_NAME, path); - goto out; + pr_err("%s: can't read file %s\n", CORESIGHT_ETM_PMU_NAME, + path); + return err; } - /* - * TRCIDR2.CIDSIZE, bit [9-5], indicates whether contextID tracing - * is supported: - * 0b00000 Context ID tracing is not supported. - * 0b00100 Maximum of 32-bit Context ID size. - * All other values are reserved. - */ - val = BMVAL(val, 5, 9); - if (!val || val != 0x4) { - err = -EINVAL; - goto out; + if (contextid & + perf_pmu__format_bits(cs_etm_pmu, "contextid1")) { + /* + * TRCIDR2.CIDSIZE, bit [9-5], indicates whether contextID + * tracing is supported: + * 0b00000 Context ID tracing is not supported. + * 0b00100 Maximum of 32-bit Context ID size. + * All other values are reserved. + */ + if (BMVAL(val, 5, 9) != 0x4) { + pr_err("%s: CONTEXTIDR_EL1 isn't supported, disable with %s/contextid1=0/\n", + CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME); + return -EINVAL; + } } - /* All good, let the kernel know */ - evsel->core.attr.config |= (1 << ETM_OPT_CTXTID); - err = 0; - -out: + if (contextid & + perf_pmu__format_bits(cs_etm_pmu, "contextid2")) { + /* + * TRCIDR2.VMIDOPT[30:29] != 0 and + * TRCIDR2.VMIDSIZE[14:10] == 0b00100 (32bit virtual contextid) + * We can't support CONTEXTIDR in VMID if the size of the + * virtual context id is < 32bit. + * Any value of VMIDSIZE >= 4 (i.e, > 32bit) is fine for us. + */ + if (!BMVAL(val, 29, 30) || BMVAL(val, 10, 14) < 4) { + pr_err("%s: CONTEXTIDR_EL2 isn't supported, disable with %s/contextid2=0/\n", + CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME); + return -EINVAL; + } + } - return err; + return 0; } -static int cs_etm_set_timestamp(struct auxtrace_record *itr, - struct evsel *evsel, int cpu) +static int cs_etm_validate_timestamp(struct auxtrace_record *itr, + struct evsel *evsel, int cpu) { - struct cs_etm_recording *ptr; - struct perf_pmu *cs_etm_pmu; + struct cs_etm_recording *ptr = + container_of(itr, struct cs_etm_recording, itr); + struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; char path[PATH_MAX]; - int err = -EINVAL; + int err; u32 val; - ptr = container_of(itr, struct cs_etm_recording, itr); - cs_etm_pmu = ptr->cs_etm_pmu; + if (!(evsel->core.attr.config & + perf_pmu__format_bits(cs_etm_pmu, "timestamp"))) + return 0; - if (!cs_etm_is_etmv4(itr, cpu)) - goto out; + if (!cs_etm_is_etmv4(itr, cpu)) { + pr_err("%s: timestamp not supported in ETMv3, disable with %s/timestamp=0/\n", + CORESIGHT_ETM_PMU_NAME, CORESIGHT_ETM_PMU_NAME); + return -EINVAL; + } /* Get a handle on TRCIRD0 */ snprintf(path, PATH_MAX, "cpu%d/%s", @@ -132,7 +169,7 @@ static int cs_etm_set_timestamp(struct auxtrace_record *itr, if (err != 1) { pr_err("%s: can't read file %s\n", CORESIGHT_ETM_PMU_NAME, path); - goto out; + return err; } /* @@ -144,43 +181,48 @@ static int cs_etm_set_timestamp(struct auxtrace_record *itr, */ val &= GENMASK(28, 24); if (!val) { - err = -EINVAL; - goto out; + return -EINVAL; } - /* All good, let the kernel know */ - evsel->core.attr.config |= (1 << ETM_OPT_TS); - err = 0; - -out: - return err; + return 0; } -static int cs_etm_set_option(struct auxtrace_record *itr, - struct evsel *evsel, u32 option) +/* + * Check whether the requested timestamp and contextid options should be + * available on all requested CPUs and if not, tell the user how to override. + * The kernel will silently disable any unavailable options so a warning here + * first is better. In theory the kernel could still disable the option for + * some other reason so this is best effort only. + */ +static int cs_etm_validate_config(struct auxtrace_record *itr, + struct evsel *evsel) { int i, err = -EINVAL; - struct perf_cpu_map *event_cpus = evsel->evlist->core.cpus; - struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); + struct perf_cpu_map *event_cpus = evsel->evlist->core.user_requested_cpus; + struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus(); /* Set option of each CPU we have */ - for (i = 0; i < cpu__max_cpu(); i++) { - if (!cpu_map__has(event_cpus, i) || - !cpu_map__has(online_cpus, i)) + for (i = 0; i < cpu__max_cpu().cpu; i++) { + struct perf_cpu cpu = { .cpu = i, }; + + /* + * In per-cpu case, do the validation for CPUs to work with. + * In per-thread case, the CPU map is empty. Since the traced + * program can run on any CPUs in this case, thus don't skip + * validation. + */ + if (!perf_cpu_map__has_any_cpu_or_is_empty(event_cpus) && + !perf_cpu_map__has(event_cpus, cpu)) continue; - if (option & ETM_OPT_CTXTID) { - err = cs_etm_set_context_id(itr, evsel, i); - if (err) - goto out; - } - if (option & ETM_OPT_TS) { - err = cs_etm_set_timestamp(itr, evsel, i); - if (err) - goto out; - } - if (option & ~(ETM_OPT_CTXTID | ETM_OPT_TS)) - /* Nothing else is currently supported */ + if (!perf_cpu_map__has(online_cpus, cpu)) + continue; + + err = cs_etm_validate_context_id(itr, evsel, i); + if (err) + goto out; + err = cs_etm_validate_timestamp(itr, evsel, i); + if (err) goto out; } @@ -232,9 +274,15 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu, ret = perf_pmu__scan_file(pmu, path, "%x", &hash); if (ret != 1) { - pr_err("failed to set sink \"%s\" on event %s with %d (%s)\n", - sink, evsel__name(evsel), errno, - str_error_r(errno, msg, sizeof(msg))); + if (errno == ENOENT) + pr_err("Couldn't find sink \"%s\" on event %s\n" + "Missing kernel or device support?\n\n" + "Hint: An appropriate sink will be picked automatically if one isn't specified.\n", + sink, evsel__name(evsel)); + else + pr_err("Failed to set sink \"%s\" on event %s with %d (%s)\n", + sink, evsel__name(evsel), errno, + str_error_r(errno, msg, sizeof(msg))); return ret; } @@ -243,10 +291,10 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu, } /* - * No sink was provided on the command line - for _now_ treat - * this as an error. + * No sink was provided on the command line - allow the CoreSight + * system to look for a default */ - return ret; + return 0; } static int cs_etm_recording_options(struct auxtrace_record *itr, @@ -258,17 +306,10 @@ static int cs_etm_recording_options(struct auxtrace_record *itr, container_of(itr, struct cs_etm_recording, itr); struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; struct evsel *evsel, *cs_etm_evsel = NULL; - struct perf_cpu_map *cpus = evlist->core.cpus; + struct perf_cpu_map *cpus = evlist->core.user_requested_cpus; bool privileged = perf_event_paranoid_check(-1); int err = 0; - ptr->evlist = evlist; - ptr->snapshot_mode = opts->auxtrace_snapshot_mode; - - if (!record_opts__no_switch_events(opts) && - perf_can_record_switch_events()) - opts->record_switch_events = true; - evlist__for_each_entry(evlist, evsel) { if (evsel->core.attr.type == cs_etm_pmu->type) { if (cs_etm_evsel) { @@ -276,10 +317,7 @@ static int cs_etm_recording_options(struct auxtrace_record *itr, CORESIGHT_ETM_PMU_NAME); return -EINVAL; } - evsel->core.attr.freq = 0; - evsel->core.attr.sample_period = 1; cs_etm_evsel = evsel; - opts->full_auxtrace = true; } } @@ -287,6 +325,16 @@ static int cs_etm_recording_options(struct auxtrace_record *itr, if (!cs_etm_evsel) return 0; + ptr->evlist = evlist; + ptr->snapshot_mode = opts->auxtrace_snapshot_mode; + + if (!record_opts__no_switch_events(opts) && + perf_can_record_switch_events()) + opts->record_switch_events = true; + + cs_etm_evsel->needs_auxtrace_mmap = true; + opts->full_auxtrace = true; + ret = cs_etm_set_sink_attr(cs_etm_pmu, cs_etm_evsel); if (ret) return ret; @@ -339,7 +387,7 @@ static int cs_etm_recording_options(struct auxtrace_record *itr, opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); } - /* Snapshost size can't be bigger than the auxtrace area */ + /* Snapshot size can't be bigger than the auxtrace area */ if (opts->auxtrace_snapshot_size > opts->auxtrace_mmap_pages * (size_t)page_size) { pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n", @@ -356,8 +404,8 @@ static int cs_etm_recording_options(struct auxtrace_record *itr, } } - /* We are in full trace mode but '-m,xyz' wasn't specified */ - if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) { + /* Buffer sizes weren't specified with '-m,xyz' so give some defaults */ + if (!opts->auxtrace_mmap_pages) { if (privileged) { opts->auxtrace_mmap_pages = MiB(4) / page_size; } else { @@ -365,26 +413,6 @@ static int cs_etm_recording_options(struct auxtrace_record *itr, if (opts->mmap_pages == UINT_MAX) opts->mmap_pages = KiB(256) / page_size; } - - } - - /* Validate auxtrace_mmap_pages provided by user */ - if (opts->auxtrace_mmap_pages) { - unsigned int max_page = (KiB(128) / page_size); - size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; - - if (!privileged && - opts->auxtrace_mmap_pages > max_page) { - opts->auxtrace_mmap_pages = max_page; - pr_err("auxtrace too big, truncating to %d\n", - max_page); - } - - if (!is_power_of_2(sz)) { - pr_err("Invalid mmap size for %s: must be a power of 2\n", - CORESIGHT_ETM_PMU_NAME); - return -EINVAL; - } } if (opts->auxtrace_snapshot_mode) @@ -395,41 +423,48 @@ static int cs_etm_recording_options(struct auxtrace_record *itr, * To obtain the auxtrace buffer file descriptor, the auxtrace * event must come first. */ - perf_evlist__to_front(evlist, cs_etm_evsel); + evlist__to_front(evlist, cs_etm_evsel); /* - * In the case of per-cpu mmaps, we need the CPU on the - * AUX event. We also need the contextID in order to be notified - * when a context switch happened. + * get the CPU on the sample - need it to associate trace ID in the + * AUX_OUTPUT_HW_ID event, and the AUX event for per-cpu mmaps. */ - if (!perf_cpu_map__empty(cpus)) { - evsel__set_sample_bit(cs_etm_evsel, CPU); + evsel__set_sample_bit(cs_etm_evsel, CPU); - err = cs_etm_set_option(itr, cs_etm_evsel, - ETM_OPT_CTXTID | ETM_OPT_TS); - if (err) - goto out; + /* + * Also the case of per-cpu mmaps, need the contextID in order to be notified + * when a context switch happened. + */ + if (!perf_cpu_map__has_any_cpu_or_is_empty(cpus)) { + evsel__set_config_if_unset(cs_etm_pmu, cs_etm_evsel, + "timestamp", 1); + evsel__set_config_if_unset(cs_etm_pmu, cs_etm_evsel, + "contextid", 1); } - /* Add dummy event to keep tracking */ - if (opts->full_auxtrace) { - struct evsel *tracking_evsel; - - err = parse_events(evlist, "dummy:u", NULL); - if (err) - goto out; - - tracking_evsel = evlist__last(evlist); - perf_evlist__set_tracking_event(evlist, tracking_evsel); + /* + * When the option '--timestamp' or '-T' is enabled, the PERF_SAMPLE_TIME + * bit is set for all events. In this case, always enable Arm CoreSight + * timestamp tracing. + */ + if (opts->sample_time_set) + evsel__set_config_if_unset(cs_etm_pmu, cs_etm_evsel, + "timestamp", 1); - tracking_evsel->core.attr.freq = 0; - tracking_evsel->core.attr.sample_period = 1; + /* Add dummy event to keep tracking */ + err = parse_event(evlist, "dummy:u"); + if (err) + goto out; + evsel = evlist__last(evlist); + evlist__set_tracking_event(evlist, evsel); + evsel->core.attr.freq = 0; + evsel->core.attr.sample_period = 1; - /* In per-cpu case, always need the time of mmap events etc */ - if (!perf_cpu_map__empty(cpus)) - evsel__set_sample_bit(tracking_evsel, TIME); - } + /* In per-cpu case, always need the time of mmap events etc */ + if (!perf_cpu_map__has_any_cpu_or_is_empty(cpus)) + evsel__set_sample_bit(evsel, TIME); + err = cs_etm_validate_config(itr, cs_etm_evsel); out: return err; } @@ -485,6 +520,11 @@ static u64 cs_etmv4_get_config(struct auxtrace_record *itr) config |= BIT(ETM4_CFG_BIT_TS); if (config_opts & BIT(ETM_OPT_RETSTK)) config |= BIT(ETM4_CFG_BIT_RETSTK); + if (config_opts & BIT(ETM_OPT_CTXTID2)) + config |= BIT(ETM4_CFG_BIT_VMID) | + BIT(ETM4_CFG_BIT_VMID_OPT); + if (config_opts & BIT(ETM_OPT_BRANCH_BROADCAST)) + config |= BIT(ETM4_CFG_BIT_BB); return config; } @@ -494,29 +534,37 @@ cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused, struct evlist *evlist __maybe_unused) { int i; - int etmv3 = 0, etmv4 = 0; - struct perf_cpu_map *event_cpus = evlist->core.cpus; - struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); + int etmv3 = 0, etmv4 = 0, ete = 0; + struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus; + struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus(); /* cpu map is not empty, we have specific CPUs to work with */ - if (!perf_cpu_map__empty(event_cpus)) { - for (i = 0; i < cpu__max_cpu(); i++) { - if (!cpu_map__has(event_cpus, i) || - !cpu_map__has(online_cpus, i)) + if (!perf_cpu_map__has_any_cpu_or_is_empty(event_cpus)) { + for (i = 0; i < cpu__max_cpu().cpu; i++) { + struct perf_cpu cpu = { .cpu = i, }; + + if (!perf_cpu_map__has(event_cpus, cpu) || + !perf_cpu_map__has(online_cpus, cpu)) continue; - if (cs_etm_is_etmv4(itr, i)) + if (cs_etm_is_ete(itr, i)) + ete++; + else if (cs_etm_is_etmv4(itr, i)) etmv4++; else etmv3++; } } else { /* get configuration for all CPUs in the system */ - for (i = 0; i < cpu__max_cpu(); i++) { - if (!cpu_map__has(online_cpus, i)) + for (i = 0; i < cpu__max_cpu().cpu; i++) { + struct perf_cpu cpu = { .cpu = i, }; + + if (!perf_cpu_map__has(online_cpus, cpu)) continue; - if (cs_etm_is_etmv4(itr, i)) + if (cs_etm_is_ete(itr, i)) + ete++; + else if (cs_etm_is_etmv4(itr, i)) etmv4++; else etmv3++; @@ -526,6 +574,7 @@ cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused, perf_cpu_map__put(online_cpus); return (CS_ETM_HEADER_SIZE + + (ete * CS_ETE_PRIV_SIZE) + (etmv4 * CS_ETMV4_PRIV_SIZE) + (etmv3 * CS_ETMV3_PRIV_SIZE)); } @@ -568,52 +617,160 @@ static int cs_etm_get_ro(struct perf_pmu *pmu, int cpu, const char *path) return val; } +static int cs_etm_get_ro_signed(struct perf_pmu *pmu, int cpu, const char *path) +{ + char pmu_path[PATH_MAX]; + int scan; + int val = 0; + + /* Get RO metadata from sysfs */ + snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); + + scan = perf_pmu__scan_file(pmu, pmu_path, "%d", &val); + if (scan != 1) + pr_err("%s: error reading: %s\n", __func__, pmu_path); + + return val; +} + +static bool cs_etm_pmu_path_exists(struct perf_pmu *pmu, int cpu, const char *path) +{ + char pmu_path[PATH_MAX]; + + /* Get RO metadata from sysfs */ + snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); + + return perf_pmu__file_exists(pmu, pmu_path); +} + +#define TRCDEVARCH_ARCHPART_SHIFT 0 +#define TRCDEVARCH_ARCHPART_MASK GENMASK(11, 0) +#define TRCDEVARCH_ARCHPART(x) (((x) & TRCDEVARCH_ARCHPART_MASK) >> TRCDEVARCH_ARCHPART_SHIFT) + +#define TRCDEVARCH_ARCHVER_SHIFT 12 +#define TRCDEVARCH_ARCHVER_MASK GENMASK(15, 12) +#define TRCDEVARCH_ARCHVER(x) (((x) & TRCDEVARCH_ARCHVER_MASK) >> TRCDEVARCH_ARCHVER_SHIFT) + +static bool cs_etm_is_ete(struct auxtrace_record *itr, int cpu) +{ + struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); + struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; + int trcdevarch; + + if (!cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCDEVARCH])) + return false; + + trcdevarch = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCDEVARCH]); + /* + * ETE if ARCHVER is 5 (ARCHVER is 4 for ETM) and ARCHPART is 0xA13. + * See ETM_DEVARCH_ETE_ARCH in coresight-etm4x.h + */ + return TRCDEVARCH_ARCHVER(trcdevarch) == 5 && TRCDEVARCH_ARCHPART(trcdevarch) == 0xA13; +} + +static void cs_etm_save_etmv4_header(__u64 data[], struct auxtrace_record *itr, int cpu) +{ + struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); + struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; + + /* Get trace configuration register */ + data[CS_ETMV4_TRCCONFIGR] = cs_etmv4_get_config(itr); + /* traceID set to legacy version, in case new perf running on older system */ + data[CS_ETMV4_TRCTRACEIDR] = + CORESIGHT_LEGACY_CPU_TRACE_ID(cpu) | CORESIGHT_TRACE_ID_UNUSED_FLAG; + + /* Get read-only information from sysFS */ + data[CS_ETMV4_TRCIDR0] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); + data[CS_ETMV4_TRCIDR1] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_etmv4_ro[CS_ETMV4_TRCIDR1]); + data[CS_ETMV4_TRCIDR2] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_etmv4_ro[CS_ETMV4_TRCIDR2]); + data[CS_ETMV4_TRCIDR8] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_etmv4_ro[CS_ETMV4_TRCIDR8]); + data[CS_ETMV4_TRCAUTHSTATUS] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_etmv4_ro[CS_ETMV4_TRCAUTHSTATUS]); + + /* Kernels older than 5.19 may not expose ts_source */ + if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TS_SOURCE])) + data[CS_ETMV4_TS_SOURCE] = (__u64) cs_etm_get_ro_signed(cs_etm_pmu, cpu, + metadata_etmv4_ro[CS_ETMV4_TS_SOURCE]); + else { + pr_debug3("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n", + cpu); + data[CS_ETMV4_TS_SOURCE] = (__u64) -1; + } +} + +static void cs_etm_save_ete_header(__u64 data[], struct auxtrace_record *itr, int cpu) +{ + struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); + struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; + + /* Get trace configuration register */ + data[CS_ETE_TRCCONFIGR] = cs_etmv4_get_config(itr); + /* traceID set to legacy version, in case new perf running on older system */ + data[CS_ETE_TRCTRACEIDR] = + CORESIGHT_LEGACY_CPU_TRACE_ID(cpu) | CORESIGHT_TRACE_ID_UNUSED_FLAG; + + /* Get read-only information from sysFS */ + data[CS_ETE_TRCIDR0] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_ete_ro[CS_ETE_TRCIDR0]); + data[CS_ETE_TRCIDR1] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_ete_ro[CS_ETE_TRCIDR1]); + data[CS_ETE_TRCIDR2] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_ete_ro[CS_ETE_TRCIDR2]); + data[CS_ETE_TRCIDR8] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_ete_ro[CS_ETE_TRCIDR8]); + data[CS_ETE_TRCAUTHSTATUS] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_ete_ro[CS_ETE_TRCAUTHSTATUS]); + /* ETE uses the same registers as ETMv4 plus TRCDEVARCH */ + data[CS_ETE_TRCDEVARCH] = cs_etm_get_ro(cs_etm_pmu, cpu, + metadata_ete_ro[CS_ETE_TRCDEVARCH]); + + /* Kernels older than 5.19 may not expose ts_source */ + if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TS_SOURCE])) + data[CS_ETE_TS_SOURCE] = (__u64) cs_etm_get_ro_signed(cs_etm_pmu, cpu, + metadata_ete_ro[CS_ETE_TS_SOURCE]); + else { + pr_debug3("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n", + cpu); + data[CS_ETE_TS_SOURCE] = (__u64) -1; + } +} + static void cs_etm_get_metadata(int cpu, u32 *offset, struct auxtrace_record *itr, struct perf_record_auxtrace_info *info) { - u32 increment; + u32 increment, nr_trc_params; u64 magic; struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; /* first see what kind of tracer this cpu is affined to */ - if (cs_etm_is_etmv4(itr, cpu)) { + if (cs_etm_is_ete(itr, cpu)) { + magic = __perf_cs_ete_magic; + cs_etm_save_ete_header(&info->priv[*offset], itr, cpu); + + /* How much space was used */ + increment = CS_ETE_PRIV_MAX; + nr_trc_params = CS_ETE_PRIV_MAX - CS_ETM_COMMON_BLK_MAX_V1; + } else if (cs_etm_is_etmv4(itr, cpu)) { magic = __perf_cs_etmv4_magic; - /* Get trace configuration register */ - info->priv[*offset + CS_ETMV4_TRCCONFIGR] = - cs_etmv4_get_config(itr); - /* Get traceID from the framework */ - info->priv[*offset + CS_ETMV4_TRCTRACEIDR] = - coresight_get_trace_id(cpu); - /* Get read-only information from sysFS */ - info->priv[*offset + CS_ETMV4_TRCIDR0] = - cs_etm_get_ro(cs_etm_pmu, cpu, - metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); - info->priv[*offset + CS_ETMV4_TRCIDR1] = - cs_etm_get_ro(cs_etm_pmu, cpu, - metadata_etmv4_ro[CS_ETMV4_TRCIDR1]); - info->priv[*offset + CS_ETMV4_TRCIDR2] = - cs_etm_get_ro(cs_etm_pmu, cpu, - metadata_etmv4_ro[CS_ETMV4_TRCIDR2]); - info->priv[*offset + CS_ETMV4_TRCIDR8] = - cs_etm_get_ro(cs_etm_pmu, cpu, - metadata_etmv4_ro[CS_ETMV4_TRCIDR8]); - info->priv[*offset + CS_ETMV4_TRCAUTHSTATUS] = - cs_etm_get_ro(cs_etm_pmu, cpu, - metadata_etmv4_ro - [CS_ETMV4_TRCAUTHSTATUS]); + cs_etm_save_etmv4_header(&info->priv[*offset], itr, cpu); /* How much space was used */ increment = CS_ETMV4_PRIV_MAX; + nr_trc_params = CS_ETMV4_PRIV_MAX - CS_ETMV4_TRCCONFIGR; } else { magic = __perf_cs_etmv3_magic; /* Get configuration register */ info->priv[*offset + CS_ETM_ETMCR] = cs_etm_get_config(itr); - /* Get traceID from the framework */ + /* traceID set to legacy value in case new perf running on old system */ info->priv[*offset + CS_ETM_ETMTRACEIDR] = - coresight_get_trace_id(cpu); + CORESIGHT_LEGACY_CPU_TRACE_ID(cpu) | CORESIGHT_TRACE_ID_UNUSED_FLAG; /* Get read-only information from sysFS */ info->priv[*offset + CS_ETM_ETMCCER] = cs_etm_get_ro(cs_etm_pmu, cpu, @@ -624,11 +781,13 @@ static void cs_etm_get_metadata(int cpu, u32 *offset, /* How much space was used */ increment = CS_ETM_PRIV_MAX; + nr_trc_params = CS_ETM_PRIV_MAX - CS_ETM_ETMCR; } /* Build generic header portion */ info->priv[*offset + CS_ETM_MAGIC] = magic; info->priv[*offset + CS_ETM_CPU] = cpu; + info->priv[*offset + CS_ETM_NR_TRC_PARAMS] = nr_trc_params; /* Where the next CPU entry should start from */ *offset += increment; } @@ -642,8 +801,8 @@ static int cs_etm_info_fill(struct auxtrace_record *itr, u32 offset; u64 nr_cpu, type; struct perf_cpu_map *cpu_map; - struct perf_cpu_map *event_cpus = session->evlist->core.cpus; - struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); + struct perf_cpu_map *event_cpus = session->evlist->core.user_requested_cpus; + struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus(); struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; @@ -655,13 +814,15 @@ static int cs_etm_info_fill(struct auxtrace_record *itr, return -EINVAL; /* If the cpu_map is empty all online CPUs are involved */ - if (perf_cpu_map__empty(event_cpus)) { + if (perf_cpu_map__has_any_cpu_or_is_empty(event_cpus)) { cpu_map = online_cpus; } else { /* Make sure all specified CPUs are online */ for (i = 0; i < perf_cpu_map__nr(event_cpus); i++) { - if (cpu_map__has(event_cpus, i) && - !cpu_map__has(online_cpus, i)) + struct perf_cpu cpu = { .cpu = i, }; + + if (perf_cpu_map__has(event_cpus, cpu) && + !perf_cpu_map__has(online_cpus, cpu)) return -EINVAL; } @@ -674,147 +835,21 @@ static int cs_etm_info_fill(struct auxtrace_record *itr, /* First fill out the session header */ info->type = PERF_AUXTRACE_CS_ETM; - info->priv[CS_HEADER_VERSION_0] = 0; + info->priv[CS_HEADER_VERSION] = CS_HEADER_CURRENT_VERSION; info->priv[CS_PMU_TYPE_CPUS] = type << 32; info->priv[CS_PMU_TYPE_CPUS] |= nr_cpu; info->priv[CS_ETM_SNAPSHOT] = ptr->snapshot_mode; offset = CS_ETM_SNAPSHOT + 1; - for (i = 0; i < cpu__max_cpu() && offset < priv_size; i++) - if (cpu_map__has(cpu_map, i)) - cs_etm_get_metadata(i, &offset, itr, info); - - perf_cpu_map__put(online_cpus); - - return 0; -} - -static int cs_etm_alloc_wrapped_array(struct cs_etm_recording *ptr, int idx) -{ - bool *wrapped; - int cnt = ptr->wrapped_cnt; + for (i = 0; i < cpu__max_cpu().cpu && offset < priv_size; i++) { + struct perf_cpu cpu = { .cpu = i, }; - /* Make @ptr->wrapped as big as @idx */ - while (cnt <= idx) - cnt++; - - /* - * Free'ed in cs_etm_recording_free(). Using realloc() to avoid - * cross compilation problems where the host's system supports - * reallocarray() but not the target. - */ - wrapped = realloc(ptr->wrapped, cnt * sizeof(bool)); - if (!wrapped) - return -ENOMEM; - - wrapped[cnt - 1] = false; - ptr->wrapped_cnt = cnt; - ptr->wrapped = wrapped; - - return 0; -} - -static bool cs_etm_buffer_has_wrapped(unsigned char *buffer, - size_t buffer_size, u64 head) -{ - u64 i, watermark; - u64 *buf = (u64 *)buffer; - size_t buf_size = buffer_size; - - /* - * We want to look the very last 512 byte (chosen arbitrarily) in - * the ring buffer. - */ - watermark = buf_size - 512; - - /* - * @head is continuously increasing - if its value is equal or greater - * than the size of the ring buffer, it has wrapped around. - */ - if (head >= buffer_size) - return true; - - /* - * The value of @head is somewhere within the size of the ring buffer. - * This can be that there hasn't been enough data to fill the ring - * buffer yet or the trace time was so long that @head has numerically - * wrapped around. To find we need to check if we have data at the very - * end of the ring buffer. We can reliably do this because mmap'ed - * pages are zeroed out and there is a fresh mapping with every new - * session. - */ - - /* @head is less than 512 byte from the end of the ring buffer */ - if (head > watermark) - watermark = head; - - /* - * Speed things up by using 64 bit transactions (see "u64 *buf" above) - */ - watermark >>= 3; - buf_size >>= 3; - - /* - * If we find trace data at the end of the ring buffer, @head has - * been there and has numerically wrapped around at least once. - */ - for (i = watermark; i < buf_size; i++) - if (buf[i]) - return true; - - return false; -} - -static int cs_etm_find_snapshot(struct auxtrace_record *itr, - int idx, struct auxtrace_mmap *mm, - unsigned char *data, - u64 *head, u64 *old) -{ - int err; - bool wrapped; - struct cs_etm_recording *ptr = - container_of(itr, struct cs_etm_recording, itr); - - /* - * Allocate memory to keep track of wrapping if this is the first - * time we deal with this *mm. - */ - if (idx >= ptr->wrapped_cnt) { - err = cs_etm_alloc_wrapped_array(ptr, idx); - if (err) - return err; - } - - /* - * Check to see if *head has wrapped around. If it hasn't only the - * amount of data between *head and *old is snapshot'ed to avoid - * bloating the perf.data file with zeros. But as soon as *head has - * wrapped around the entire size of the AUX ring buffer it taken. - */ - wrapped = ptr->wrapped[idx]; - if (!wrapped && cs_etm_buffer_has_wrapped(data, mm->len, *head)) { - wrapped = true; - ptr->wrapped[idx] = true; + if (perf_cpu_map__has(cpu_map, cpu)) + cs_etm_get_metadata(i, &offset, itr, info); } - pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n", - __func__, idx, (size_t)*old, (size_t)*head, mm->len); - - /* No wrap has occurred, we can just use *head and *old. */ - if (!wrapped) - return 0; - - /* - * *head has wrapped around - adjust *head and *old to pickup the - * entire content of the AUX buffer. - */ - if (*head >= mm->len) { - *old = *head - mm->len; - } else { - *head += mm->len; - *old = *head - mm->len; - } + perf_cpu_map__put(online_cpus); return 0; } @@ -856,7 +891,6 @@ static void cs_etm_recording_free(struct auxtrace_record *itr) struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); - zfree(&ptr->wrapped); free(ptr); } @@ -865,7 +899,7 @@ struct auxtrace_record *cs_etm_record_init(int *err) struct perf_pmu *cs_etm_pmu; struct cs_etm_recording *ptr; - cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); + cs_etm_pmu = perf_pmus__find(CORESIGHT_ETM_PMU_NAME); if (!cs_etm_pmu) { *err = -EINVAL; @@ -884,7 +918,6 @@ struct auxtrace_record *cs_etm_record_init(int *err) ptr->itr.recording_options = cs_etm_recording_options; ptr->itr.info_priv_size = cs_etm_info_priv_size; ptr->itr.info_fill = cs_etm_info_fill; - ptr->itr.find_snapshot = cs_etm_find_snapshot; ptr->itr.snapshot_start = cs_etm_snapshot_start; ptr->itr.snapshot_finish = cs_etm_snapshot_finish; ptr->itr.reference = cs_etm_reference; @@ -896,3 +929,15 @@ struct auxtrace_record *cs_etm_record_init(int *err) out: return NULL; } + +/* + * Set a default config to enable the user changed config tracking mechanism + * (CFG_CHG and evsel__set_config_if_unset()). If no default is set then user + * changes aren't tracked. + */ +void +cs_etm_get_default_config(const struct perf_pmu *pmu __maybe_unused, + struct perf_event_attr *attr) +{ + attr->sample_period = 1; +} |