diff options
Diffstat (limited to 'tools/perf/arch/arm/util')
-rw-r--r-- | tools/perf/arch/arm/util/Build | 10 | ||||
-rw-r--r-- | tools/perf/arch/arm/util/auxtrace.c | 171 | ||||
-rw-r--r-- | tools/perf/arch/arm/util/cs-etm.c | 836 | ||||
-rw-r--r-- | tools/perf/arch/arm/util/dwarf-regs.c | 61 | ||||
-rw-r--r-- | tools/perf/arch/arm/util/perf_regs.c | 20 | ||||
-rw-r--r-- | tools/perf/arch/arm/util/pmu.c | 30 | ||||
-rw-r--r-- | tools/perf/arch/arm/util/unwind-libdw.c | 9 | ||||
-rw-r--r-- | tools/perf/arch/arm/util/unwind-libunwind.c | 4 |
8 files changed, 620 insertions, 521 deletions
diff --git a/tools/perf/arch/arm/util/Build b/tools/perf/arch/arm/util/Build index 37fc63708966..f7a8b37d1c68 100644 --- a/tools/perf/arch/arm/util/Build +++ b/tools/perf/arch/arm/util/Build @@ -1,8 +1,6 @@ -perf-y += perf_regs.o +perf-util-y += perf_regs.o -perf-$(CONFIG_DWARF) += dwarf-regs.o +perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o +perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o -perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o -perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o - -perf-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs-etm.o +perf-util-$(CONFIG_AUXTRACE) += pmu.o auxtrace.o cs-etm.o diff --git a/tools/perf/arch/arm/util/auxtrace.c b/tools/perf/arch/arm/util/auxtrace.c index 0a6e75b8777a..3b8eca0ffb17 100644 --- a/tools/perf/arch/arm/util/auxtrace.c +++ b/tools/perf/arch/arm/util/auxtrace.c @@ -4,16 +4,20 @@ * Author: Mathieu Poirier <mathieu.poirier@linaro.org> */ +#include <dirent.h> #include <stdbool.h> #include <linux/coresight-pmu.h> #include <linux/zalloc.h> +#include <api/fs/fs.h> -#include "../../util/auxtrace.h" -#include "../../util/debug.h" -#include "../../util/evlist.h" -#include "../../util/pmu.h" +#include "../../../util/auxtrace.h" +#include "../../../util/debug.h" +#include "../../../util/evlist.h" +#include "../../../util/pmu.h" +#include "../../../util/pmus.h" #include "cs-etm.h" #include "arm-spe.h" +#include "hisi-ptt.h" static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) { @@ -37,7 +41,7 @@ static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) return NULL; } - arm_spe_pmus[*nr_spes] = perf_pmu__find(arm_spe_pmu_name); + arm_spe_pmus[*nr_spes] = perf_pmus__find(arm_spe_pmu_name); if (arm_spe_pmus[*nr_spes]) { pr_debug2("%s %d: arm_spe_pmu %d type %d name %s\n", __func__, __LINE__, *nr_spes, @@ -50,43 +54,113 @@ static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) return arm_spe_pmus; } +static struct perf_pmu **find_all_hisi_ptt_pmus(int *nr_ptts, int *err) +{ + struct perf_pmu **hisi_ptt_pmus = NULL; + struct dirent *dent; + char path[PATH_MAX]; + DIR *dir = NULL; + int idx = 0; + + perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); + dir = opendir(path); + if (!dir) { + pr_err("can't read directory '%s'\n", path); + *err = -EINVAL; + return NULL; + } + + while ((dent = readdir(dir))) { + if (strstr(dent->d_name, HISI_PTT_PMU_NAME)) + (*nr_ptts)++; + } + + if (!(*nr_ptts)) + goto out; + + hisi_ptt_pmus = zalloc(sizeof(struct perf_pmu *) * (*nr_ptts)); + if (!hisi_ptt_pmus) { + pr_err("hisi_ptt alloc failed\n"); + *err = -ENOMEM; + goto out; + } + + rewinddir(dir); + while ((dent = readdir(dir))) { + if (strstr(dent->d_name, HISI_PTT_PMU_NAME) && idx < *nr_ptts) { + hisi_ptt_pmus[idx] = perf_pmus__find(dent->d_name); + if (hisi_ptt_pmus[idx]) + idx++; + } + } + +out: + closedir(dir); + return hisi_ptt_pmus; +} + +static struct perf_pmu *find_pmu_for_event(struct perf_pmu **pmus, + int pmu_nr, struct evsel *evsel) +{ + int i; + + if (!pmus) + return NULL; + + for (i = 0; i < pmu_nr; i++) { + if (evsel->core.attr.type == pmus[i]->type) + return pmus[i]; + } + + return NULL; +} + struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, int *err) { - struct perf_pmu *cs_etm_pmu; + struct perf_pmu *cs_etm_pmu = NULL; + struct perf_pmu **arm_spe_pmus = NULL; + struct perf_pmu **hisi_ptt_pmus = NULL; struct evsel *evsel; - bool found_etm = false; - bool found_spe = false; - static struct perf_pmu **arm_spe_pmus = NULL; - static int nr_spes = 0; - int i = 0; + struct perf_pmu *found_etm = NULL; + struct perf_pmu *found_spe = NULL; + struct perf_pmu *found_ptt = NULL; + int auxtrace_event_cnt = 0; + int nr_spes = 0; + int nr_ptts = 0; if (!evlist) return NULL; - cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); - - if (!arm_spe_pmus) - arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err); + cs_etm_pmu = perf_pmus__find(CORESIGHT_ETM_PMU_NAME); + arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err); + hisi_ptt_pmus = find_all_hisi_ptt_pmus(&nr_ptts, err); evlist__for_each_entry(evlist, evsel) { - if (cs_etm_pmu && - evsel->core.attr.type == cs_etm_pmu->type) - found_etm = true; - - if (!nr_spes) - continue; - - for (i = 0; i < nr_spes; i++) { - if (evsel->core.attr.type == arm_spe_pmus[i]->type) { - found_spe = true; - break; - } - } + if (cs_etm_pmu && !found_etm) + found_etm = find_pmu_for_event(&cs_etm_pmu, 1, evsel); + + if (arm_spe_pmus && !found_spe) + found_spe = find_pmu_for_event(arm_spe_pmus, nr_spes, evsel); + + if (hisi_ptt_pmus && !found_ptt) + found_ptt = find_pmu_for_event(hisi_ptt_pmus, nr_ptts, evsel); } - if (found_etm && found_spe) { - pr_err("Concurrent ARM Coresight ETM and SPE operation not currently supported\n"); + free(arm_spe_pmus); + free(hisi_ptt_pmus); + + if (found_etm) + auxtrace_event_cnt++; + + if (found_spe) + auxtrace_event_cnt++; + + if (found_ptt) + auxtrace_event_cnt++; + + if (auxtrace_event_cnt > 1) { + pr_err("Concurrent AUX trace operation not currently supported\n"); *err = -EOPNOTSUPP; return NULL; } @@ -96,7 +170,10 @@ struct auxtrace_record #if defined(__aarch64__) if (found_spe) - return arm_spe_recording_init(err, arm_spe_pmus[i]); + return arm_spe_recording_init(err, found_spe); + + if (found_ptt) + return hisi_ptt_recording_init(err, found_ptt); #endif /* @@ -108,3 +185,35 @@ struct auxtrace_record *err = 0; return NULL; } + +#if defined(__arm__) +u64 compat_auxtrace_mmap__read_head(struct auxtrace_mmap *mm) +{ + struct perf_event_mmap_page *pc = mm->userpg; + u64 result; + + __asm__ __volatile__( +" ldrd %0, %H0, [%1]" + : "=&r" (result) + : "r" (&pc->aux_head), "Qo" (pc->aux_head) + ); + + return result; +} + +int compat_auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail) +{ + struct perf_event_mmap_page *pc = mm->userpg; + + /* Ensure all reads are done before we write the tail out */ + smp_mb(); + + __asm__ __volatile__( +" strd %2, %H2, [%1]" + : "=Qo" (pc->aux_tail) + : "r" (&pc->aux_tail), "r" (tail) + ); + + return 0; +} +#endif diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index cea5e33d61d2..ea891d12f8f4 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,91 +47,124 @@ 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 bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu); +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 int cs_etm_set_context_id(struct auxtrace_record *itr, - struct evsel *evsel, int cpu) -{ - struct cs_etm_recording *ptr; - struct perf_pmu *cs_etm_pmu; - char path[PATH_MAX]; - int err = -EINVAL; - u32 val; +enum cs_etm_version { CS_NOT_PRESENT, CS_ETMV3, CS_ETMV4, CS_ETE }; - ptr = container_of(itr, struct cs_etm_recording, itr); - cs_etm_pmu = ptr->cs_etm_pmu; +static bool cs_etm_is_ete(struct perf_pmu *cs_etm_pmu, struct perf_cpu cpu); +static int cs_etm_get_ro(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path, __u64 *val); +static bool cs_etm_pmu_path_exists(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path); - if (!cs_etm_is_etmv4(itr, cpu)) - goto out; +static enum cs_etm_version cs_etm_get_version(struct perf_pmu *cs_etm_pmu, + struct perf_cpu cpu) +{ + if (cs_etm_is_ete(cs_etm_pmu, cpu)) + return CS_ETE; + else if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0])) + return CS_ETMV4; + else if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_etmv3_ro[CS_ETM_ETMCCER])) + return CS_ETMV3; + + return CS_NOT_PRESENT; +} - /* Get a handle on TRCIRD2 */ - 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); +static int cs_etm_validate_context_id(struct perf_pmu *cs_etm_pmu, struct evsel *evsel, + struct perf_cpu cpu) +{ + int err; + __u64 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")); - /* 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; - } + if (!contextid) + return 0; - /* - * 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; + /* Not supported in etmv3 */ + if (cs_etm_get_version(cs_etm_pmu, cpu) == CS_ETMV3) { + pr_err("%s: contextid not supported in ETMv3, disable with %s/contextid=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; + /* Get a handle on TRCIDR2 */ + err = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR2], &val); + if (err) + return err; -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; + } + } - return err; + 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 0; } -static int cs_etm_set_timestamp(struct auxtrace_record *itr, - struct evsel *evsel, int cpu) +static int cs_etm_validate_timestamp(struct perf_pmu *cs_etm_pmu, struct evsel *evsel, + struct perf_cpu cpu) { - struct cs_etm_recording *ptr; - struct perf_pmu *cs_etm_pmu; - char path[PATH_MAX]; - int err = -EINVAL; - u32 val; + int err; + __u64 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_get_version(cs_etm_pmu, cpu) == CS_ETMV3) { + 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", - cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); - 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; - } + err = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0], &val); + if (err) + return err; /* * TRCIDR0.TSSIZE, bit [28-24], indicates whether global timestamping @@ -144,49 +175,65 @@ 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; + return 0; +} + +static struct perf_pmu *cs_etm_get_pmu(struct auxtrace_record *itr) +{ + struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); -out: - return err; + return ptr->cs_etm_pmu; } -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 perf_pmu *cs_etm_pmu, + 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); - - /* 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)) - continue; + int idx, err = 0; + struct perf_cpu_map *event_cpus = evsel->evlist->core.user_requested_cpus; + struct perf_cpu_map *intersect_cpus; + struct perf_cpu cpu; - 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; + /* + * Set option of each CPU we have. In per-cpu case, do the validation + * for CPUs to work with. In per-thread case, the CPU map has the "any" + * CPU value. Since the traced program can run on any CPUs in this case, + * thus don't skip validation. + */ + if (!perf_cpu_map__has_any_cpu(event_cpus)) { + struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus(); + + intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus); + perf_cpu_map__put(online_cpus); + } else { + intersect_cpus = perf_cpu_map__new_online_cpus(); + } + + perf_cpu_map__for_each_cpu_skip_any(cpu, idx, intersect_cpus) { + if (cs_etm_get_version(cs_etm_pmu, cpu) == CS_NOT_PRESENT) { + pr_err("%s: Not found on CPU %d. Check hardware and firmware support and that all Coresight drivers are loaded\n", + CORESIGHT_ETM_PMU_NAME, cpu.cpu); + return -EINVAL; } - if (option & ~(ETM_OPT_CTXTID | ETM_OPT_TS)) - /* Nothing else is currently supported */ - goto out; + err = cs_etm_validate_context_id(cs_etm_pmu, evsel, cpu); + if (err) + break; + + err = cs_etm_validate_timestamp(cs_etm_pmu, evsel, cpu); + if (err) + break; } - err = 0; -out: - perf_cpu_map__put(online_cpus); + perf_cpu_map__put(intersect_cpus); return err; } @@ -232,9 +279,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 +296,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 +311,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 +322,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 +330,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 +392,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 +409,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 +418,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 +428,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__is_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__is_any_cpu_or_is_empty(cpus)) + evsel__set_sample_bit(evsel, TIME); + err = cs_etm_validate_config(cs_etm_pmu, cs_etm_evsel); out: return err; } @@ -485,150 +525,247 @@ 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; } static size_t -cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused, - struct evlist *evlist __maybe_unused) +cs_etm_info_priv_size(struct auxtrace_record *itr, + struct evlist *evlist) { - 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); - - /* 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)) - continue; - - if (cs_etm_is_etmv4(itr, i)) - etmv4++; - else - etmv3++; - } + int idx; + int etmv3 = 0, etmv4 = 0, ete = 0; + struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus; + struct perf_cpu_map *intersect_cpus; + struct perf_cpu cpu; + struct perf_pmu *cs_etm_pmu = cs_etm_get_pmu(itr); + + if (!perf_cpu_map__has_any_cpu(event_cpus)) { + /* cpu map is not "any" CPU , we have specific CPUs to work with */ + struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus(); + + intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus); + perf_cpu_map__put(online_cpus); } else { - /* get configuration for all CPUs in the system */ - for (i = 0; i < cpu__max_cpu(); i++) { - if (!cpu_map__has(online_cpus, i)) - continue; - - if (cs_etm_is_etmv4(itr, i)) - etmv4++; - else - etmv3++; - } + /* Event can be "any" CPU so count all online CPUs. */ + intersect_cpus = perf_cpu_map__new_online_cpus(); } + /* Count number of each type of ETM. Don't count if that CPU has CS_NOT_PRESENT. */ + perf_cpu_map__for_each_cpu_skip_any(cpu, idx, intersect_cpus) { + enum cs_etm_version v = cs_etm_get_version(cs_etm_pmu, cpu); - perf_cpu_map__put(online_cpus); + ete += v == CS_ETE; + etmv4 += v == CS_ETMV4; + etmv3 += v == CS_ETMV3; + } + perf_cpu_map__put(intersect_cpus); return (CS_ETM_HEADER_SIZE + + (ete * CS_ETE_PRIV_SIZE) + (etmv4 * CS_ETMV4_PRIV_SIZE) + (etmv3 * CS_ETMV3_PRIV_SIZE)); } -static bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu) +static int cs_etm_get_ro(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path, __u64 *val) { - bool ret = false; - char path[PATH_MAX]; + char pmu_path[PATH_MAX]; int scan; - unsigned int val; - struct cs_etm_recording *ptr = - container_of(itr, struct cs_etm_recording, itr); - struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; - /* Take any of the RO files for ETMv4 and see if it present */ - snprintf(path, PATH_MAX, "cpu%d/%s", - cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0]); - scan = perf_pmu__scan_file(cs_etm_pmu, path, "%x", &val); + /* Get RO metadata from sysfs */ + snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.cpu, path); - /* The file was read successfully, we have a winner */ - if (scan == 1) - ret = true; + scan = perf_pmu__scan_file(pmu, pmu_path, "%llx", val); + if (scan != 1) { + pr_err("%s: error reading: %s\n", __func__, pmu_path); + return -EINVAL; + } - return ret; + return 0; } -static int cs_etm_get_ro(struct perf_pmu *pmu, int cpu, const char *path) +static int cs_etm_get_ro_signed(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path, + __u64 *out_val) { char pmu_path[PATH_MAX]; int scan; - unsigned int val = 0; + int val = 0; /* Get RO metadata from sysfs */ - snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); + snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.cpu, path); - scan = perf_pmu__scan_file(pmu, pmu_path, "%x", &val); - if (scan != 1) + scan = perf_pmu__scan_file(pmu, pmu_path, "%d", &val); + if (scan != 1) { pr_err("%s: error reading: %s\n", __func__, pmu_path); + return -EINVAL; + } - return val; + *out_val = (__u64) val; + return 0; } -static void cs_etm_get_metadata(int cpu, u32 *offset, +static bool cs_etm_pmu_path_exists(struct perf_pmu *pmu, struct perf_cpu cpu, const char *path) +{ + char pmu_path[PATH_MAX]; + + /* Get RO metadata from sysfs */ + snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu.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 perf_pmu *cs_etm_pmu, struct perf_cpu cpu) +{ + __u64 trcdevarch; + + if (!cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCDEVARCH])) + return false; + + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCDEVARCH], &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 __u64 cs_etm_get_legacy_trace_id(struct perf_cpu cpu) +{ + /* Wrap at 48 so that invalid trace IDs aren't saved into files. */ + return CORESIGHT_LEGACY_CPU_TRACE_ID(cpu.cpu % 48); +} + +static void cs_etm_save_etmv4_header(__u64 data[], struct auxtrace_record *itr, struct perf_cpu 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] = cs_etm_get_legacy_trace_id(cpu); + + /* Get read-only information from sysFS */ + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0], + &data[CS_ETMV4_TRCIDR0]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR1], + &data[CS_ETMV4_TRCIDR1]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR2], + &data[CS_ETMV4_TRCIDR2]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR8], + &data[CS_ETMV4_TRCIDR8]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCAUTHSTATUS], + &data[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]) || + cs_etm_get_ro_signed(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TS_SOURCE], + &data[CS_ETMV4_TS_SOURCE])) { + pr_debug3("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n", + cpu.cpu); + data[CS_ETMV4_TS_SOURCE] = (__u64) -1; + } +} + +static void cs_etm_save_ete_header(__u64 data[], struct auxtrace_record *itr, struct perf_cpu 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] = cs_etm_get_legacy_trace_id(cpu); + + /* Get read-only information from sysFS */ + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCIDR0], &data[CS_ETE_TRCIDR0]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCIDR1], &data[CS_ETE_TRCIDR1]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCIDR2], &data[CS_ETE_TRCIDR2]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCIDR8], &data[CS_ETE_TRCIDR8]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCAUTHSTATUS], + &data[CS_ETE_TRCAUTHSTATUS]); + /* ETE uses the same registers as ETMv4 plus TRCDEVARCH */ + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCDEVARCH], + &data[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]) || + cs_etm_get_ro_signed(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TS_SOURCE], + &data[CS_ETE_TS_SOURCE])) { + pr_debug3("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n", + cpu.cpu); + data[CS_ETE_TS_SOURCE] = (__u64) -1; + } +} + +static void cs_etm_get_metadata(struct perf_cpu 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; + struct perf_pmu *cs_etm_pmu = cs_etm_get_pmu(itr); /* first see what kind of tracer this cpu is affined to */ - if (cs_etm_is_etmv4(itr, cpu)) { + switch (cs_etm_get_version(cs_etm_pmu, cpu)) { + case CS_ETE: + 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; + break; + + case CS_ETMV4: 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; - } else { + nr_trc_params = CS_ETMV4_PRIV_MAX - CS_ETMV4_TRCCONFIGR; + break; + + case CS_ETMV3: magic = __perf_cs_etmv3_magic; /* Get configuration register */ info->priv[*offset + CS_ETM_ETMCR] = cs_etm_get_config(itr); - /* Get traceID from the framework */ - info->priv[*offset + CS_ETM_ETMTRACEIDR] = - coresight_get_trace_id(cpu); + /* traceID set to legacy value in case new perf running on old system */ + info->priv[*offset + CS_ETM_ETMTRACEIDR] = cs_etm_get_legacy_trace_id(cpu); /* Get read-only information from sysFS */ - info->priv[*offset + CS_ETM_ETMCCER] = - cs_etm_get_ro(cs_etm_pmu, cpu, - metadata_etmv3_ro[CS_ETM_ETMCCER]); - info->priv[*offset + CS_ETM_ETMIDR] = - cs_etm_get_ro(cs_etm_pmu, cpu, - metadata_etmv3_ro[CS_ETM_ETMIDR]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv3_ro[CS_ETM_ETMCCER], + &info->priv[*offset + CS_ETM_ETMCCER]); + cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv3_ro[CS_ETM_ETMIDR], + &info->priv[*offset + CS_ETM_ETMIDR]); /* How much space was used */ increment = CS_ETM_PRIV_MAX; + nr_trc_params = CS_ETM_PRIV_MAX - CS_ETM_ETMCR; + break; + + default: + case CS_NOT_PRESENT: + /* Unreachable, CPUs already validated in cs_etm_validate_config() */ + assert(true); + return; } /* Build generic header portion */ info->priv[*offset + CS_ETM_MAGIC] = magic; - info->priv[*offset + CS_ETM_CPU] = cpu; + info->priv[*offset + CS_ETM_CPU] = cpu.cpu; + info->priv[*offset + CS_ETM_NR_TRC_PARAMS] = nr_trc_params; /* Where the next CPU entry should start from */ *offset += increment; } @@ -642,11 +779,12 @@ 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; + struct perf_cpu cpu; if (priv_size != cs_etm_info_priv_size(itr, session->evlist)) return -EINVAL; @@ -654,14 +792,13 @@ static int cs_etm_info_fill(struct auxtrace_record *itr, if (!session->evlist->core.nr_mmaps) return -EINVAL; - /* If the cpu_map is empty all online CPUs are involved */ - if (perf_cpu_map__empty(event_cpus)) { + /* If the cpu_map has the "any" CPU all online CPUs are involved */ + if (perf_cpu_map__has_any_cpu(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)) + perf_cpu_map__for_each_cpu(cpu, i, event_cpus) { + if (!perf_cpu_map__has(online_cpus, cpu)) return -EINVAL; } @@ -674,147 +811,19 @@ 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; - - /* 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; + perf_cpu_map__for_each_cpu(cpu, i, cpu_map) { + assert(offset < priv_size); + cs_etm_get_metadata(cpu, &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 +865,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 +873,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; @@ -879,12 +887,10 @@ struct auxtrace_record *cs_etm_record_init(int *err) } ptr->cs_etm_pmu = cs_etm_pmu; - ptr->itr.pmu = cs_etm_pmu; ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options; 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 +902,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; +} diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c deleted file mode 100644 index fc5f71c91802..000000000000 --- a/tools/perf/arch/arm/util/dwarf-regs.c +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2010 Will Deacon, ARM Ltd. - */ - -#include <stddef.h> -#include <linux/stringify.h> -#include <dwarf-regs.h> - -struct pt_regs_dwarfnum { - const char *name; - unsigned int dwarfnum; -}; - -#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} -#define GPR_DWARFNUM_NAME(num) \ - {.name = __stringify(%r##num), .dwarfnum = num} -#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} - -/* - * Reference: - * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf - */ -static const struct pt_regs_dwarfnum regdwarfnum_table[] = { - GPR_DWARFNUM_NAME(0), - GPR_DWARFNUM_NAME(1), - GPR_DWARFNUM_NAME(2), - GPR_DWARFNUM_NAME(3), - GPR_DWARFNUM_NAME(4), - GPR_DWARFNUM_NAME(5), - GPR_DWARFNUM_NAME(6), - GPR_DWARFNUM_NAME(7), - GPR_DWARFNUM_NAME(8), - GPR_DWARFNUM_NAME(9), - GPR_DWARFNUM_NAME(10), - REG_DWARFNUM_NAME("%fp", 11), - REG_DWARFNUM_NAME("%ip", 12), - REG_DWARFNUM_NAME("%sp", 13), - REG_DWARFNUM_NAME("%lr", 14), - REG_DWARFNUM_NAME("%pc", 15), - REG_DWARFNUM_END, -}; - -/** - * get_arch_regstr() - lookup register name from it's DWARF register number - * @n: the DWARF register number - * - * get_arch_regstr() returns the name of the register in struct - * regdwarfnum_table from it's DWARF register number. If the register is not - * found in the table, this returns NULL; - */ -const char *get_arch_regstr(unsigned int n) -{ - const struct pt_regs_dwarfnum *roff; - for (roff = regdwarfnum_table; roff->name != NULL; roff++) - if (roff->dwarfnum == n) - return roff->name; - return NULL; -} diff --git a/tools/perf/arch/arm/util/perf_regs.c b/tools/perf/arch/arm/util/perf_regs.c index 2864e2e3776d..f94a0210c7b7 100644 --- a/tools/perf/arch/arm/util/perf_regs.c +++ b/tools/perf/arch/arm/util/perf_regs.c @@ -1,6 +1,22 @@ // SPDX-License-Identifier: GPL-2.0 -#include "../../util/perf_regs.h" +#include "perf_regs.h" +#include "../../../util/perf_regs.h" -const struct sample_reg sample_reg_masks[] = { +static const struct sample_reg sample_reg_masks[] = { SMPL_REG_END }; + +uint64_t arch__intr_reg_mask(void) +{ + return PERF_REGS_MASK; +} + +uint64_t arch__user_reg_mask(void) +{ + return PERF_REGS_MASK; +} + +const struct sample_reg *arch__sample_reg_masks(void) +{ + return sample_reg_masks; +} diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c index bbc297a7e2e3..f70075c89aa0 100644 --- a/tools/perf/arch/arm/util/pmu.c +++ b/tools/perf/arch/arm/util/pmu.c @@ -10,21 +10,39 @@ #include <linux/string.h> #include "arm-spe.h" -#include "../../util/pmu.h" +#include "hisi-ptt.h" +#include "../../../util/cpumap.h" +#include "../../../util/pmu.h" +#include "../../../util/cs-etm.h" +#include "../../arm64/util/mem-events.h" -struct perf_event_attr -*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) +void perf_pmu__arch_init(struct perf_pmu *pmu) { + struct perf_cpu_map *intersect, *online = cpu_map__online(); + #ifdef HAVE_AUXTRACE_SUPPORT if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) { /* add ETM default config here */ + pmu->auxtrace = true; pmu->selectable = true; + pmu->perf_event_attr_init_default = cs_etm_get_default_config; #if defined(__aarch64__) } else if (strstarts(pmu->name, ARM_SPE_PMU_NAME)) { - return arm_spe_pmu_default_config(pmu); + pmu->auxtrace = true; + pmu->selectable = true; + pmu->is_uncore = false; + pmu->perf_event_attr_init_default = arm_spe_pmu_default_config; + if (strstarts(pmu->name, "arm_spe_")) + pmu->mem_events = perf_mem_events_arm; + } else if (strstarts(pmu->name, HISI_PTT_PMU_NAME)) { + pmu->auxtrace = true; + pmu->selectable = true; #endif } - #endif - return NULL; + /* Workaround some ARM PMU's failing to correctly set CPU maps for online processors. */ + intersect = perf_cpu_map__intersect(online, pmu->cpus); + perf_cpu_map__put(online); + perf_cpu_map__put(pmu->cpus); + pmu->cpus = intersect; } diff --git a/tools/perf/arch/arm/util/unwind-libdw.c b/tools/perf/arch/arm/util/unwind-libdw.c index 36ba4c69c3c5..fbb643f224ec 100644 --- a/tools/perf/arch/arm/util/unwind-libdw.c +++ b/tools/perf/arch/arm/util/unwind-libdw.c @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 #include <elfutils/libdwfl.h> -#include "../../util/unwind-libdw.h" -#include "../../util/perf_regs.h" -#include "../../util/event.h" +#include "perf_regs.h" +#include "../../../util/unwind-libdw.h" +#include "../../../util/perf_regs.h" +#include "../../../util/sample.h" bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg) { struct unwind_info *ui = arg; - struct regs_dump *user_regs = &ui->sample->user_regs; + struct regs_dump *user_regs = perf_sample__user_regs(ui->sample); Dwarf_Word dwarf_regs[PERF_REG_ARM_MAX]; #define REG(r) ({ \ diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c index 3a550225dfaf..438906bf0014 100644 --- a/tools/perf/arch/arm/util/unwind-libunwind.c +++ b/tools/perf/arch/arm/util/unwind-libunwind.c @@ -3,8 +3,8 @@ #include <errno.h> #include <libunwind.h> #include "perf_regs.h" -#include "../../util/unwind.h" -#include "../../util/debug.h" +#include "../../../util/unwind.h" +#include "../../../util/debug.h" int libunwind__arch_reg_id(int regnum) { |