aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tools/perf/arch/arm/util/cs-etm.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/arch/arm/util/cs-etm.c')
-rw-r--r--tools/perf/arch/arm/util/cs-etm.c697
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;
+}