/* Iterate config list to detect if the "freq" parameter is set */ staticbool arm_spe_is_set_freq(struct evsel *evsel)
{ struct evsel_config_term *term;
list_for_each_entry(term, &evsel->config_terms, list) { if (term->type == EVSEL__CONFIG_TERM_FREQ) returntrue;
}
returnfalse;
}
/* * arm_spe_find_cpus() returns a new cpu map, and the caller should invoke * perf_cpu_map__put() to release the map after use.
*/ staticstruct perf_cpu_map *arm_spe_find_cpus(struct evlist *evlist)
{ struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus; struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus(); struct perf_cpu_map *intersect_cpus;
/* cpu map is not "any" CPU , we have specific CPUs to work with */ if (!perf_cpu_map__has_any_cpu(event_cpus)) {
intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus);
perf_cpu_map__put(online_cpus); /* Event can be "any" CPU so count all CPUs. */
} else {
intersect_cpus = online_cpus;
}
/* Find the associate Arm SPE PMU for the CPU */ if (perf_cpu_map__has(sper->arm_spe_pmu->cpus, cpu))
pmu = sper->arm_spe_pmu;
if (!pmu) { /* No Arm SPE PMU is found */
data[ARM_SPE_CPU_PMU_TYPE] = ULLONG_MAX;
data[ARM_SPE_CAP_MIN_IVAL] = 0;
} else {
data[ARM_SPE_CPU_PMU_TYPE] = pmu->type;
if (perf_pmu__scan_file(pmu, "caps/min_interval", "%lu", &val) != 1)
val = 0;
data[ARM_SPE_CAP_MIN_IVAL] = val;
}
offset = ARM_SPE_AUXTRACE_PRIV_MAX;
perf_cpu_map__for_each_cpu(cpu, i, cpu_map) {
assert(offset < priv_size);
data = &auxtrace_info->priv[offset];
ret = arm_spe_save_cpu_header(itr, cpu, data); if (ret < 0) goto out;
offset += ret;
}
ret = 0;
out:
perf_cpu_map__put(cpu_map); return ret;
}
staticvoid
arm_spe_snapshot_resolve_auxtrace_defaults(struct record_opts *opts, bool privileged)
{ /* * The default snapshot size is the auxtrace mmap size. If neither auxtrace mmap size nor * snapshot size is specified, then the default is 4MiB for privileged users, 128KiB for * unprivileged users. * * The default auxtrace mmap size is 4MiB/page_size for privileged users, 128KiB for * unprivileged users. If an unprivileged user does not specify mmap pages, the mmap pages * will be reduced from the default 512KiB/page_size to 256KiB/page_size, otherwise the * user is likely to get an error as they exceed their mlock limmit.
*/
/* * No size were given to '-S' or '-m,', so go with the default
*/ if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) { if (privileged) {
opts->auxtrace_mmap_pages = MiB(4) / page_size;
} else {
opts->auxtrace_mmap_pages = KiB(128) / page_size; if (opts->mmap_pages == UINT_MAX)
opts->mmap_pages = KiB(256) / page_size;
}
} elseif (!opts->auxtrace_mmap_pages && !privileged && opts->mmap_pages == UINT_MAX) {
opts->mmap_pages = KiB(256) / page_size;
}
/* * '-m,xyz' was specified but no snapshot size, so make the snapshot size as big as the * auxtrace mmap area.
*/ if (!opts->auxtrace_snapshot_size)
opts->auxtrace_snapshot_size = opts->auxtrace_mmap_pages * (size_t)page_size;
/* * '-Sxyz' was specified but no auxtrace mmap area, so make the auxtrace mmap area big * enough to fit the requested snapshot size.
*/ if (!opts->auxtrace_mmap_pages) {
size_t sz = opts->auxtrace_snapshot_size;
/* * If kernel driver doesn't advertise a minimum, * use max allowable by PMSIDR_EL1.INTERVAL
*/ if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu",
&sample_period) != 1) {
pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n");
sample_period = 4096;
} return sample_period;
}
/* * To obtain the auxtrace buffer file descriptor, the auxtrace event * must come first.
*/
evlist__to_front(evsel->evlist, evsel);
/* * In the case of per-cpu mmaps, sample CPU for AUX event; * also enable the timestamp tracing for samples correlation.
*/ if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) {
evsel__set_sample_bit(evsel, CPU);
evsel__set_config_if_unset(evsel->pmu, evsel, "ts_enable", 1);
}
/* * Set this only so that perf report knows that SPE generates memory info. It has no effect * on the opening of the event or the SPE data produced.
*/
evsel__set_sample_bit(evsel, DATA_SRC);
/* * The PHYS_ADDR flag does not affect the driver behaviour, it is used to * inform that the resulting output's SPE samples contain physical addresses * where applicable.
*/
bit = perf_pmu__format_bits(evsel->pmu, "pa_enable"); if (evsel->core.attr.config & bit)
evsel__set_sample_bit(evsel, PHYS_ADDR);
}
/* * we are in snapshot mode.
*/ if (opts->auxtrace_snapshot_mode) { /* * Command arguments '-Sxyz' and/or '-m,xyz' are missing, so fill those in with * default values.
*/ if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages)
arm_spe_snapshot_resolve_auxtrace_defaults(opts, privileged);
/* * 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",
opts->auxtrace_snapshot_size,
opts->auxtrace_mmap_pages * (size_t)page_size); return -EINVAL;
}
/* * Something went wrong somewhere - this shouldn't happen.
*/ if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) {
pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n"); return -EINVAL;
}
if (sz < min_sz || !is_power_of_2(sz)) {
pr_err("Invalid mmap size for ARM SPE: must be at least %zuKiB and a power of 2\n",
min_sz / 1024); return -EINVAL;
}
}
/* 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(tracking_evsel, TIME);
evsel__set_sample_bit(tracking_evsel, CPU);
/* also track task context switch */ if (!record_opts__no_switch_events(opts))
tracking_evsel->core.attr.context_switch = 1;
}
if (opts->user_freq != UINT_MAX ||
arm_spe_is_set_freq(evsel)) {
pr_err("Arm SPE: Frequency is not supported. " "Set period with -c option or PMU parameter (-e %s/period=NUM/).\n",
evsel->pmu->name); return -EINVAL;
}
}
}
if (!opts->full_auxtrace) return 0;
evlist__for_each_entry_safe(evlist, tmp, evsel) { if (evsel__is_aux_event(evsel)) {
arm_spe_setup_evsel(evsel, cpus); if (evsel->core.attr.config &
perf_pmu__format_bits(evsel->pmu, "discard"))
discard = true;
}
}
if (discard) return 0;
err = arm_spe_setup_aux_buffer(opts); if (err) return err;
/* * Defensively handle the case where head might be continually increasing - if its value is * equal or greater than the size of the ring buffer, then we can safely determine it has * wrapped around. Otherwise, continue to detect if head might have wrapped.
*/ if (head >= buffer_size) returntrue;
/* * We want to look the very last 512 byte (chosen arbitrarily) in the ring buffer.
*/
watermark = buf_size - 512;
/* * 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 /= sizeof(u64);
buf_size /= sizeof(u64);
/* * 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]) returntrue;
/* * Allocate memory to keep track of wrapping if this is the first * time we deal with this *mm.
*/ if (idx >= ptr->wrapped_cnt) {
err = arm_spe_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 && arm_spe_buffer_has_wrapped(data, mm->len, *head)) {
wrapped = true;
ptr->wrapped[idx] = true;
}
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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.