diff options
Diffstat (limited to 'tools/perf/arch')
-rw-r--r-- | tools/perf/arch/arm64/util/arm-spe.c | 5 | ||||
-rw-r--r-- | tools/perf/arch/arm64/util/arm64_exception_types.h | 15 | ||||
-rw-r--r-- | tools/perf/arch/powerpc/util/Build | 1 | ||||
-rw-r--r-- | tools/perf/arch/powerpc/util/auxtrace.c | 103 | ||||
-rw-r--r-- | tools/perf/arch/x86/annotate/instructions.c | 4 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/evsel.c | 114 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/kvm-stat.c | 51 |
7 files changed, 282 insertions, 11 deletions
diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index 4f2833b62ff5..cac43cde7dbe 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -121,12 +121,17 @@ static int arm_spe_save_cpu_header(struct auxtrace_record *itr, /* No Arm SPE PMU is found */ data[ARM_SPE_CPU_PMU_TYPE] = ULLONG_MAX; data[ARM_SPE_CAP_MIN_IVAL] = 0; + data[ARM_SPE_CAP_EVENT_FILTER] = 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; + + if (perf_pmu__scan_file(pmu, "caps/event_filter", "%lx", &val) != 1) + val = 0; + data[ARM_SPE_CAP_EVENT_FILTER] = val; } free(cpuid); diff --git a/tools/perf/arch/arm64/util/arm64_exception_types.h b/tools/perf/arch/arm64/util/arm64_exception_types.h index 27c981ebe401..bf827f19ace0 100644 --- a/tools/perf/arch/arm64/util/arm64_exception_types.h +++ b/tools/perf/arch/arm64/util/arm64_exception_types.h @@ -31,9 +31,10 @@ #define ESR_ELx_EC_FP_ASIMD (0x07) #define ESR_ELx_EC_CP10_ID (0x08) /* EL2 only */ #define ESR_ELx_EC_PAC (0x09) /* EL2 and above */ -/* Unallocated EC: 0x0A - 0x0B */ +#define ESR_ELx_EC_OTHER (0x0A) +/* Unallocated EC: 0x0B */ #define ESR_ELx_EC_CP14_64 (0x0C) -/* Unallocated EC: 0x0d */ +#define ESR_ELx_EC_BTI (0x0D) #define ESR_ELx_EC_ILL (0x0E) /* Unallocated EC: 0x0F - 0x10 */ #define ESR_ELx_EC_SVC32 (0x11) @@ -46,7 +47,10 @@ #define ESR_ELx_EC_SYS64 (0x18) #define ESR_ELx_EC_SVE (0x19) #define ESR_ELx_EC_ERET (0x1a) /* EL2 only */ -/* Unallocated EC: 0x1b - 0x1E */ +/* Unallocated EC: 0x1B */ +#define ESR_ELx_EC_FPAC (0x1C) /* EL1 and above */ +#define ESR_ELx_EC_SME (0x1D) +/* Unallocated EC: 0x1E */ #define ESR_ELx_EC_IMP_DEF (0x1f) /* EL3 only */ #define ESR_ELx_EC_IABT_LOW (0x20) #define ESR_ELx_EC_IABT_CUR (0x21) @@ -55,11 +59,12 @@ #define ESR_ELx_EC_DABT_LOW (0x24) #define ESR_ELx_EC_DABT_CUR (0x25) #define ESR_ELx_EC_SP_ALIGN (0x26) -/* Unallocated EC: 0x27 */ +#define ESR_ELx_EC_MOPS (0x27) #define ESR_ELx_EC_FP_EXC32 (0x28) /* Unallocated EC: 0x29 - 0x2B */ #define ESR_ELx_EC_FP_EXC64 (0x2C) -/* Unallocated EC: 0x2D - 0x2E */ +#define ESR_ELx_EC_GCS (0x2D) +/* Unallocated EC: 0x2E */ #define ESR_ELx_EC_SERROR (0x2F) #define ESR_ELx_EC_BREAKPT_LOW (0x30) #define ESR_ELx_EC_BREAKPT_CUR (0x31) diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build index fdd6a77a3432..a5b0babd307e 100644 --- a/tools/perf/arch/powerpc/util/Build +++ b/tools/perf/arch/powerpc/util/Build @@ -10,3 +10,4 @@ perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o perf-util-$(CONFIG_LIBUNWIND) += unwind-libunwind.o perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o +perf-util-$(CONFIG_AUXTRACE) += auxtrace.o diff --git a/tools/perf/arch/powerpc/util/auxtrace.c b/tools/perf/arch/powerpc/util/auxtrace.c new file mode 100644 index 000000000000..62c6f67f1bbe --- /dev/null +++ b/tools/perf/arch/powerpc/util/auxtrace.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VPA support + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> + +#include "../../util/evlist.h" +#include "../../util/debug.h" +#include "../../util/auxtrace.h" +#include "../../util/powerpc-vpadtl.h" +#include "../../util/record.h" +#include <internal/lib.h> // page_size + +#define KiB(x) ((x) * 1024) + +static int +powerpc_vpadtl_recording_options(struct auxtrace_record *ar __maybe_unused, + struct evlist *evlist __maybe_unused, + struct record_opts *opts) +{ + opts->full_auxtrace = true; + + /* + * Set auxtrace_mmap_pages to minimum + * two pages + */ + if (!opts->auxtrace_mmap_pages) { + opts->auxtrace_mmap_pages = KiB(128) / page_size; + if (opts->mmap_pages == UINT_MAX) + opts->mmap_pages = KiB(256) / page_size; + } + + return 0; +} + +static size_t powerpc_vpadtl_info_priv_size(struct auxtrace_record *itr __maybe_unused, + struct evlist *evlist __maybe_unused) +{ + return VPADTL_AUXTRACE_PRIV_SIZE; +} + +static int +powerpc_vpadtl_info_fill(struct auxtrace_record *itr __maybe_unused, + struct perf_session *session __maybe_unused, + struct perf_record_auxtrace_info *auxtrace_info, + size_t priv_size __maybe_unused) +{ + auxtrace_info->type = PERF_AUXTRACE_VPA_DTL; + + return 0; +} + +static void powerpc_vpadtl_free(struct auxtrace_record *itr) +{ + free(itr); +} + +static u64 powerpc_vpadtl_reference(struct auxtrace_record *itr __maybe_unused) +{ + return 0; +} + +struct auxtrace_record *auxtrace_record__init(struct evlist *evlist, + int *err) +{ + struct auxtrace_record *aux; + struct evsel *pos; + int found = 0; + + evlist__for_each_entry(evlist, pos) { + if (strstarts(pos->name, "vpa_dtl")) { + found = 1; + pos->needs_auxtrace_mmap = true; + break; + } + } + + if (!found) + return NULL; + + /* + * To obtain the auxtrace buffer file descriptor, the auxtrace event + * must come first. + */ + evlist__to_front(pos->evlist, pos); + + aux = zalloc(sizeof(*aux)); + if (aux == NULL) { + pr_debug("aux record is NULL\n"); + *err = -ENOMEM; + return NULL; + } + + aux->recording_options = powerpc_vpadtl_recording_options; + aux->info_priv_size = powerpc_vpadtl_info_priv_size; + aux->info_fill = powerpc_vpadtl_info_fill; + aux->free = powerpc_vpadtl_free; + aux->reference = powerpc_vpadtl_reference; + return aux; +} diff --git a/tools/perf/arch/x86/annotate/instructions.c b/tools/perf/arch/x86/annotate/instructions.c index c6d403eae744..da98a4e3c52c 100644 --- a/tools/perf/arch/x86/annotate/instructions.c +++ b/tools/perf/arch/x86/annotate/instructions.c @@ -301,7 +301,7 @@ static void update_insn_state_x86(struct type_state *state, * as a pointer. */ tsr->type = type_die; - tsr->kind = TSR_KIND_POINTER; + tsr->kind = TSR_KIND_PERCPU_POINTER; tsr->ok = true; pr_debug_dtp("add [%x] percpu %#"PRIx64" -> reg%d", @@ -521,7 +521,7 @@ retry: } /* And then dereference the calculated pointer if it has one */ else if (has_reg_type(state, sreg) && state->regs[sreg].ok && - state->regs[sreg].kind == TSR_KIND_POINTER && + state->regs[sreg].kind == TSR_KIND_PERCPU_POINTER && die_get_member_type(&state->regs[sreg].type, src->offset, &type_die)) { tsr->type = type_die; diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index 9bc80fff3aa0..23a8e662a912 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -1,10 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 +#include <errno.h> #include <stdio.h> #include <stdlib.h> +#include "util/evlist.h" #include "util/evsel.h" +#include "util/evsel_config.h" #include "util/env.h" #include "util/pmu.h" #include "util/pmus.h" +#include "util/stat.h" +#include "util/strbuf.h" #include "linux/string.h" #include "topdown.h" #include "evsel.h" @@ -67,6 +72,57 @@ int arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size) event_name); } +void arch_evsel__apply_ratio_to_prev(struct evsel *evsel, + struct perf_event_attr *attr) +{ + struct perf_event_attr *prev_attr = NULL; + struct evsel *evsel_prev = NULL; + const char *name = "acr_mask"; + int evsel_idx = 0; + __u64 ev_mask, pr_ev_mask; + + if (!perf_pmu__has_format(evsel->pmu, name)) { + pr_err("'%s' does not have acr_mask format support\n", evsel->pmu->name); + return; + } + if (perf_pmu__format_type(evsel->pmu, name) != + PERF_PMU_FORMAT_VALUE_CONFIG2) { + pr_err("'%s' does not have config2 format support\n", evsel->pmu->name); + return; + } + + evsel_prev = evsel__prev(evsel); + if (!evsel_prev) { + pr_err("Previous event does not exist.\n"); + return; + } + + prev_attr = &evsel_prev->core.attr; + + if (prev_attr->config2) { + pr_err("'%s' has set config2 (acr_mask?) already, configuration not supported\n", evsel_prev->name); + return; + } + + /* + * acr_mask (config2) is calculated using the event's index in + * the event group. The first event will use the index of the + * second event as its mask (e.g., 0x2), indicating that the + * second event counter will be reset and a sample taken for + * the first event if its counter overflows. The second event + * will use the mask consisting of the first and second bits + * (e.g., 0x3), meaning both counters will be reset if the + * second event counter overflows. + */ + + evsel_idx = evsel__group_idx(evsel); + ev_mask = 1ull << evsel_idx; + pr_ev_mask = 1ull << (evsel_idx - 1); + + prev_attr->config2 = ev_mask; + attr->config2 = ev_mask | pr_ev_mask; +} + static void ibs_l3miss_warn(void) { pr_warning( @@ -102,13 +158,15 @@ void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr) } } -int arch_evsel__open_strerror(struct evsel *evsel, char *msg, size_t size) +static int amd_evsel__open_strerror(struct evsel *evsel, char *msg, size_t size) { - if (!x86__is_amd_cpu()) + struct perf_pmu *pmu; + + if (evsel->core.attr.precise_ip == 0) return 0; - if (!evsel->core.attr.precise_ip && - !(evsel->pmu && !strncmp(evsel->pmu->name, "ibs", 3))) + pmu = evsel__find_pmu(evsel); + if (!pmu || strncmp(pmu->name, "ibs", 3)) return 0; /* More verbose IBS errors. */ @@ -118,6 +176,54 @@ int arch_evsel__open_strerror(struct evsel *evsel, char *msg, size_t size) return scnprintf(msg, size, "AMD IBS doesn't support privilege filtering. Try " "again without the privilege modifiers (like 'k') at the end."); } + return 0; +} + +static int intel_evsel__open_strerror(struct evsel *evsel, int err, char *msg, size_t size) +{ + struct strbuf sb = STRBUF_INIT; + int ret; + + if (err != EINVAL) + return 0; + if (!topdown_sys_has_perf_metrics()) + return 0; + + if (arch_is_topdown_slots(evsel)) { + if (!evsel__is_group_leader(evsel)) { + evlist__uniquify_evsel_names(evsel->evlist, &stat_config); + evlist__format_evsels(evsel->evlist, &sb, 2048); + ret = scnprintf(msg, size, "Topdown slots event can only be group leader " + "in '%s'.", sb.buf); + strbuf_release(&sb); + return ret; + } + } else if (arch_is_topdown_metrics(evsel)) { + struct evsel *pos; + + evlist__for_each_entry(evsel->evlist, pos) { + if (pos == evsel || !arch_is_topdown_metrics(pos)) + continue; + + if (pos->core.attr.config != evsel->core.attr.config) + continue; + + evlist__uniquify_evsel_names(evsel->evlist, &stat_config); + evlist__format_evsels(evsel->evlist, &sb, 2048); + ret = scnprintf(msg, size, "Perf metric event '%s' is duplicated " + "in the same group (only one event is allowed) in '%s'.", + evsel__name(evsel), sb.buf); + strbuf_release(&sb); + return ret; + } + } return 0; } + +int arch_evsel__open_strerror(struct evsel *evsel, int err, char *msg, size_t size) +{ + return x86__is_amd_cpu() + ? amd_evsel__open_strerror(evsel, msg, size) + : intel_evsel__open_strerror(evsel, err, msg, size); +} diff --git a/tools/perf/arch/x86/util/kvm-stat.c b/tools/perf/arch/x86/util/kvm-stat.c index 424716518b75..bff36f9345ea 100644 --- a/tools/perf/arch/x86/util/kvm-stat.c +++ b/tools/perf/arch/x86/util/kvm-stat.c @@ -3,9 +3,11 @@ #include <string.h> #include "../../../util/kvm-stat.h" #include "../../../util/evsel.h" +#include "../../../util/env.h" #include <asm/svm.h> #include <asm/vmx.h> #include <asm/kvm.h> +#include <subcmd/parse-options.h> define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); @@ -211,3 +213,52 @@ int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) return 0; } + +/* + * After KVM supports PEBS for guest on Intel platforms + * (https://lore.kernel.org/all/20220411101946.20262-1-likexu@tencent.com/), + * host loses the capability to sample guest with PEBS since all PEBS related + * MSRs are switched to guest value after vm-entry, like IA32_DS_AREA MSR is + * switched to guest GVA at vm-entry. This would lead to "perf kvm record" + * fails to sample guest on Intel platforms since "cycles:P" event is used to + * sample guest by default. + * + * So, to avoid this issue explicitly use "cycles" instead of "cycles:P" event + * by default to sample guest on Intel platforms. + */ +int kvm_add_default_arch_event(int *argc, const char **argv) +{ + const char **tmp; + bool event = false; + int ret = 0, i, j = *argc; + + const struct option event_options[] = { + OPT_BOOLEAN('e', "event", &event, NULL), + OPT_BOOLEAN(0, "pfm-events", &event, NULL), + OPT_END() + }; + + if (!x86__is_intel_cpu()) + return 0; + + tmp = calloc(j + 1, sizeof(char *)); + if (!tmp) + return -ENOMEM; + + for (i = 0; i < j; i++) + tmp[i] = argv[i]; + + parse_options(j, tmp, event_options, NULL, PARSE_OPT_KEEP_UNKNOWN); + if (!event) { + argv[j++] = STRDUP_FAIL_EXIT("-e"); + argv[j++] = STRDUP_FAIL_EXIT("cycles"); + *argc += 2; + } + + free(tmp); + return 0; + +EXIT: + free(tmp); + return ret; +} |