diff options
Diffstat (limited to 'tools/perf/arch')
-rw-r--r-- | tools/perf/arch/powerpc/util/Build | 1 | ||||
-rw-r--r-- | tools/perf/arch/powerpc/util/event.c | 60 | ||||
-rw-r--r-- | tools/perf/arch/x86/Build | 2 | ||||
-rw-r--r-- | tools/perf/arch/x86/include/arch-tests.h | 5 | ||||
-rw-r--r-- | tools/perf/arch/x86/tests/Build | 4 | ||||
-rw-r--r-- | tools/perf/arch/x86/tests/arch-tests.c | 3 | ||||
-rw-r--r-- | tools/perf/arch/x86/tests/sample-parsing.c | 125 | ||||
-rw-r--r-- | tools/perf/arch/x86/tests/topdown.c | 76 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/event.c | 46 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/evlist.c | 24 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/evsel.c | 46 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/topdown.c | 59 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/topdown.h | 6 |
13 files changed, 164 insertions, 293 deletions
diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build index ed82715080f9..fdd6a77a3432 100644 --- a/tools/perf/arch/powerpc/util/Build +++ b/tools/perf/arch/powerpc/util/Build @@ -5,7 +5,6 @@ perf-util-y += mem-events.o perf-util-y += pmu.o perf-util-y += sym-handling.o perf-util-y += evsel.o -perf-util-y += event.o perf-util-$(CONFIG_LIBDW) += skip-callchain-idx.o diff --git a/tools/perf/arch/powerpc/util/event.c b/tools/perf/arch/powerpc/util/event.c deleted file mode 100644 index 77d8cc2b5691..000000000000 --- a/tools/perf/arch/powerpc/util/event.c +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/types.h> -#include <linux/string.h> -#include <linux/zalloc.h> - -#include "../../../util/event.h" -#include "../../../util/synthetic-events.h" -#include "../../../util/machine.h" -#include "../../../util/tool.h" -#include "../../../util/map.h" -#include "../../../util/debug.h" -#include "../../../util/sample.h" - -void arch_perf_parse_sample_weight(struct perf_sample *data, - const __u64 *array, u64 type) -{ - union perf_sample_weight weight; - - weight.full = *array; - if (type & PERF_SAMPLE_WEIGHT) - data->weight = weight.full; - else { - data->weight = weight.var1_dw; - data->ins_lat = weight.var2_w; - data->p_stage_cyc = weight.var3_w; - } -} - -void arch_perf_synthesize_sample_weight(const struct perf_sample *data, - __u64 *array, u64 type) -{ - *array = data->weight; - - if (type & PERF_SAMPLE_WEIGHT_STRUCT) { - *array &= 0xffffffff; - *array |= ((u64)data->ins_lat << 32); - } -} - -const char *arch_perf_header_entry(const char *se_header) -{ - if (!strcmp(se_header, "Local INSTR Latency")) - return "Finish Cyc"; - else if (!strcmp(se_header, "INSTR Latency")) - return "Global Finish_cyc"; - else if (!strcmp(se_header, "Local Pipeline Stage Cycle")) - return "Dispatch Cyc"; - else if (!strcmp(se_header, "Pipeline Stage Cycle")) - return "Global Dispatch_cyc"; - return se_header; -} - -int arch_support_sort_key(const char *sort_key) -{ - if (!strcmp(sort_key, "p_stage_cyc")) - return 1; - if (!strcmp(sort_key, "local_p_stage_cyc")) - return 1; - return 0; -} diff --git a/tools/perf/arch/x86/Build b/tools/perf/arch/x86/Build index afae7b8f6bd6..d31a1168757c 100644 --- a/tools/perf/arch/x86/Build +++ b/tools/perf/arch/x86/Build @@ -10,6 +10,6 @@ endif $(OUTPUT)%.shellcheck_log: % $(call rule_mkdir) - $(Q)$(call echo-cmd,test)shellcheck -a -S warning "$<" > $@ || (cat $@ && rm $@ && false) + $(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false) perf-test-y += $(SHELL_TEST_LOGS) diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h index 4fd425157d7d..7d65b9e51840 100644 --- a/tools/perf/arch/x86/include/arch-tests.h +++ b/tools/perf/arch/x86/include/arch-tests.h @@ -2,6 +2,8 @@ #ifndef ARCH_TESTS_H #define ARCH_TESTS_H +#include "tests/tests.h" + struct test_suite; /* Tests */ @@ -12,11 +14,12 @@ int test__insn_x86(struct test_suite *test, int subtest); int test__intel_pt_pkt_decoder(struct test_suite *test, int subtest); int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest); int test__bp_modify(struct test_suite *test, int subtest); -int test__x86_sample_parsing(struct test_suite *test, int subtest); int test__amd_ibs_via_core_pmu(struct test_suite *test, int subtest); int test__amd_ibs_period(struct test_suite *test, int subtest); int test__hybrid(struct test_suite *test, int subtest); +DECLARE_SUITE(x86_topdown); + extern struct test_suite *arch_tests[]; #endif diff --git a/tools/perf/arch/x86/tests/Build b/tools/perf/arch/x86/tests/Build index 5e00cbfd2d56..7790b3e20f4e 100644 --- a/tools/perf/arch/x86/tests/Build +++ b/tools/perf/arch/x86/tests/Build @@ -2,7 +2,6 @@ perf-test-$(CONFIG_DWARF_UNWIND) += regs_load.o perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o perf-test-y += arch-tests.o -perf-test-y += sample-parsing.o perf-test-y += hybrid.o perf-test-$(CONFIG_AUXTRACE) += intel-pt-test.o ifeq ($(CONFIG_EXTRA_TESTS),y) @@ -11,6 +10,7 @@ endif perf-test-$(CONFIG_X86_64) += bp-modify.o perf-test-y += amd-ibs-via-core-pmu.o perf-test-y += amd-ibs-period.o +perf-test-y += topdown.o ifdef SHELLCHECK SHELL_TESTS := gen-insn-x86-dat.sh @@ -22,6 +22,6 @@ endif $(OUTPUT)%.shellcheck_log: % $(call rule_mkdir) - $(Q)$(call echo-cmd,test)shellcheck -a -S warning "$<" > $@ || (cat $@ && rm $@ && false) + $(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false) perf-test-y += $(SHELL_TEST_LOGS) diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c index bfee2432515b..8f9cfeaa170f 100644 --- a/tools/perf/arch/x86/tests/arch-tests.c +++ b/tools/perf/arch/x86/tests/arch-tests.c @@ -23,7 +23,6 @@ struct test_suite suite__intel_pt = { #if defined(__x86_64__) DEFINE_SUITE("x86 bp modify", bp_modify); #endif -DEFINE_SUITE("x86 Sample parsing", x86_sample_parsing); DEFINE_SUITE("AMD IBS via core pmu", amd_ibs_via_core_pmu); DEFINE_SUITE_EXCLUSIVE("AMD IBS sample period", amd_ibs_period); static struct test_case hybrid_tests[] = { @@ -49,9 +48,9 @@ struct test_suite *arch_tests[] = { #if defined(__x86_64__) &suite__bp_modify, #endif - &suite__x86_sample_parsing, &suite__amd_ibs_via_core_pmu, &suite__amd_ibs_period, &suite__hybrid, + &suite__x86_topdown, NULL, }; diff --git a/tools/perf/arch/x86/tests/sample-parsing.c b/tools/perf/arch/x86/tests/sample-parsing.c deleted file mode 100644 index a061e8619267..000000000000 --- a/tools/perf/arch/x86/tests/sample-parsing.c +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include <stdbool.h> -#include <inttypes.h> -#include <stdlib.h> -#include <string.h> -#include <linux/bitops.h> -#include <linux/kernel.h> -#include <linux/types.h> - -#include "event.h" -#include "evsel.h" -#include "debug.h" -#include "util/sample.h" -#include "util/synthetic-events.h" - -#include "tests/tests.h" -#include "arch-tests.h" - -#define COMP(m) do { \ - if (s1->m != s2->m) { \ - pr_debug("Samples differ at '"#m"'\n"); \ - return false; \ - } \ -} while (0) - -static bool samples_same(const struct perf_sample *s1, - const struct perf_sample *s2, - u64 type) -{ - if (type & PERF_SAMPLE_WEIGHT_STRUCT) { - COMP(ins_lat); - COMP(retire_lat); - } - - return true; -} - -static int do_test(u64 sample_type) -{ - struct evsel evsel = { - .needs_swap = false, - .core = { - . attr = { - .sample_type = sample_type, - .read_format = 0, - }, - }, - }; - union perf_event *event; - struct perf_sample sample = { - .weight = 101, - .ins_lat = 102, - .retire_lat = 103, - }; - struct perf_sample sample_out; - size_t i, sz, bufsz; - int err, ret = -1; - - sz = perf_event__sample_event_size(&sample, sample_type, 0); - bufsz = sz + 4096; /* Add a bit for overrun checking */ - event = malloc(bufsz); - if (!event) { - pr_debug("malloc failed\n"); - return -1; - } - - memset(event, 0xff, bufsz); - event->header.type = PERF_RECORD_SAMPLE; - event->header.misc = 0; - event->header.size = sz; - - err = perf_event__synthesize_sample(event, sample_type, 0, &sample); - if (err) { - pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", - "perf_event__synthesize_sample", sample_type, err); - goto out_free; - } - - /* The data does not contain 0xff so we use that to check the size */ - for (i = bufsz; i > 0; i--) { - if (*(i - 1 + (u8 *)event) != 0xff) - break; - } - if (i != sz) { - pr_debug("Event size mismatch: actual %zu vs expected %zu\n", - i, sz); - goto out_free; - } - - evsel.sample_size = __evsel__sample_size(sample_type); - - err = evsel__parse_sample(&evsel, event, &sample_out); - if (err) { - pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", - "evsel__parse_sample", sample_type, err); - goto out_free; - } - - if (!samples_same(&sample, &sample_out, sample_type)) { - pr_debug("parsing failed for sample_type %#"PRIx64"\n", - sample_type); - goto out_free; - } - - ret = 0; -out_free: - free(event); - - return ret; -} - -/** - * test__x86_sample_parsing - test X86 specific sample parsing - * - * This function implements a test that synthesizes a sample event, parses it - * and then checks that the parsed sample matches the original sample. If the - * test passes %0 is returned, otherwise %-1 is returned. - * - * For now, the PERF_SAMPLE_WEIGHT_STRUCT is the only X86 specific sample type. - * The test only checks the PERF_SAMPLE_WEIGHT_STRUCT type. - */ -int test__x86_sample_parsing(struct test_suite *test __maybe_unused, int subtest __maybe_unused) -{ - return do_test(PERF_SAMPLE_WEIGHT_STRUCT); -} diff --git a/tools/perf/arch/x86/tests/topdown.c b/tools/perf/arch/x86/tests/topdown.c new file mode 100644 index 000000000000..8d0ea7a4bbc1 --- /dev/null +++ b/tools/perf/arch/x86/tests/topdown.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "arch-tests.h" +#include "../util/topdown.h" +#include "evlist.h" +#include "parse-events.h" +#include "pmu.h" +#include "pmus.h" + +static int event_cb(void *state, struct pmu_event_info *info) +{ + char buf[256]; + struct parse_events_error parse_err; + int *ret = state, err; + struct evlist *evlist = evlist__new(); + struct evsel *evsel; + + if (!evlist) + return -ENOMEM; + + parse_events_error__init(&parse_err); + snprintf(buf, sizeof(buf), "%s/%s/", info->pmu->name, info->name); + err = parse_events(evlist, buf, &parse_err); + if (err) { + parse_events_error__print(&parse_err, buf); + *ret = TEST_FAIL; + } + parse_events_error__exit(&parse_err); + evlist__for_each_entry(evlist, evsel) { + bool fail = false; + bool p_core_pmu = evsel->pmu->type == PERF_TYPE_RAW; + const char *name = evsel__name(evsel); + + if (strcasestr(name, "uops_retired.slots") || + strcasestr(name, "topdown.backend_bound_slots") || + strcasestr(name, "topdown.br_mispredict_slots") || + strcasestr(name, "topdown.memory_bound_slots") || + strcasestr(name, "topdown.bad_spec_slots") || + strcasestr(name, "topdown.slots_p")) { + if (arch_is_topdown_slots(evsel) || arch_is_topdown_metrics(evsel)) + fail = true; + } else if (strcasestr(name, "slots")) { + if (arch_is_topdown_slots(evsel) != p_core_pmu || + arch_is_topdown_metrics(evsel)) + fail = true; + } else if (strcasestr(name, "topdown")) { + if (arch_is_topdown_slots(evsel) || + arch_is_topdown_metrics(evsel) != p_core_pmu) + fail = true; + } else if (arch_is_topdown_slots(evsel) || arch_is_topdown_metrics(evsel)) { + fail = true; + } + if (fail) { + pr_debug("Broken topdown information for '%s'\n", evsel__name(evsel)); + *ret = TEST_FAIL; + } + } + evlist__delete(evlist); + return 0; +} + +static int test__x86_topdown(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +{ + int ret = TEST_OK; + struct perf_pmu *pmu = NULL; + + if (!topdown_sys_has_perf_metrics()) + return TEST_OK; + + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { + if (perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/false, &ret, event_cb)) + break; + } + return ret; +} + +DEFINE_SUITE("x86 topdown", x86_topdown); diff --git a/tools/perf/arch/x86/util/event.c b/tools/perf/arch/x86/util/event.c index a0400707180c..3cd384317739 100644 --- a/tools/perf/arch/x86/util/event.c +++ b/tools/perf/arch/x86/util/event.c @@ -91,49 +91,3 @@ int perf_event__synthesize_extra_kmaps(const struct perf_tool *tool, } #endif - -void arch_perf_parse_sample_weight(struct perf_sample *data, - const __u64 *array, u64 type) -{ - union perf_sample_weight weight; - - weight.full = *array; - if (type & PERF_SAMPLE_WEIGHT) - data->weight = weight.full; - else { - data->weight = weight.var1_dw; - data->ins_lat = weight.var2_w; - data->retire_lat = weight.var3_w; - } -} - -void arch_perf_synthesize_sample_weight(const struct perf_sample *data, - __u64 *array, u64 type) -{ - *array = data->weight; - - if (type & PERF_SAMPLE_WEIGHT_STRUCT) { - *array &= 0xffffffff; - *array |= ((u64)data->ins_lat << 32); - *array |= ((u64)data->retire_lat << 48); - } -} - -const char *arch_perf_header_entry(const char *se_header) -{ - if (!strcmp(se_header, "Local Pipeline Stage Cycle")) - return "Local Retire Latency"; - else if (!strcmp(se_header, "Pipeline Stage Cycle")) - return "Retire Latency"; - - return se_header; -} - -int arch_support_sort_key(const char *sort_key) -{ - if (!strcmp(sort_key, "p_stage_cyc")) - return 1; - if (!strcmp(sort_key, "local_p_stage_cyc")) - return 1; - return 0; -} diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index 1969758cc8c1..75e9d00a1494 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -81,3 +81,27 @@ int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs) /* Default ordering by insertion index. */ return lhs->core.idx - rhs->core.idx; } + +int arch_evlist__add_required_events(struct list_head *list) +{ + struct evsel *pos, *metric_event = NULL; + int idx = 0; + + if (!topdown_sys_has_perf_metrics()) + return 0; + + list_for_each_entry(pos, list, core.node) { + if (arch_is_topdown_slots(pos)) { + /* Slots event already present, nothing to do. */ + return 0; + } + if (metric_event == NULL && arch_is_topdown_metrics(pos)) + metric_event = pos; + idx++; + } + if (metric_event == NULL) { + /* No topdown metric events, nothing to do. */ + return 0; + } + return topdown_insert_slots_event(list, idx + 1, metric_event); +} diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index 3dd29ba2c23b..9bc80fff3aa0 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -23,47 +23,25 @@ void arch_evsel__set_sample_weight(struct evsel *evsel) bool evsel__sys_has_perf_metrics(const struct evsel *evsel) { struct perf_pmu *pmu; - u32 type = evsel->core.attr.type; - /* - * The PERF_TYPE_RAW type is the core PMU type, e.g., "cpu" PMU - * on a non-hybrid machine, "cpu_core" PMU on a hybrid machine. - * The slots event is only available for the core PMU, which - * supports the perf metrics feature. - * Checking both the PERF_TYPE_RAW type and the slots event - * should be good enough to detect the perf metrics feature. - */ -again: - switch (type) { - case PERF_TYPE_HARDWARE: - case PERF_TYPE_HW_CACHE: - type = evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT; - if (type) - goto again; - break; - case PERF_TYPE_RAW: - break; - default: + if (!topdown_sys_has_perf_metrics()) return false; - } - - pmu = evsel->pmu; - if (pmu && perf_pmu__is_fake(pmu)) - pmu = NULL; - if (!pmu) { - while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { - if (pmu->type == PERF_TYPE_RAW) - break; - } - } - return pmu && perf_pmu__have_event(pmu, "slots"); + /* + * The PERF_TYPE_RAW type is the core PMU type, e.g., "cpu" PMU on a + * non-hybrid machine, "cpu_core" PMU on a hybrid machine. The + * topdown_sys_has_perf_metrics checks the slots event is only available + * for the core PMU, which supports the perf metrics feature. Checking + * both the PERF_TYPE_RAW type and the slots event should be good enough + * to detect the perf metrics feature. + */ + pmu = evsel__find_pmu(evsel); + return pmu && pmu->type == PERF_TYPE_RAW; } bool arch_evsel__must_be_in_group(const struct evsel *evsel) { - if (!evsel__sys_has_perf_metrics(evsel) || !evsel->name || - strcasestr(evsel->name, "uops_retired.slots")) + if (!evsel__sys_has_perf_metrics(evsel)) return false; return arch_is_topdown_metrics(evsel) || arch_is_topdown_slots(evsel); diff --git a/tools/perf/arch/x86/util/topdown.c b/tools/perf/arch/x86/util/topdown.c index d1c654839049..0d01b662627a 100644 --- a/tools/perf/arch/x86/util/topdown.c +++ b/tools/perf/arch/x86/util/topdown.c @@ -1,6 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include "api/fs/fs.h" -#include "util/evsel.h" #include "util/evlist.h" #include "util/pmu.h" #include "util/pmus.h" @@ -8,6 +6,9 @@ #include "topdown.h" #include "evsel.h" +// cmask=0, inv=0, pc=0, edge=0, umask=4, event=0 +#define TOPDOWN_SLOTS 0x0400 + /* Check whether there is a PMU which supports the perf metrics. */ bool topdown_sys_has_perf_metrics(void) { @@ -32,31 +33,19 @@ bool topdown_sys_has_perf_metrics(void) return has_perf_metrics; } -#define TOPDOWN_SLOTS 0x0400 bool arch_is_topdown_slots(const struct evsel *evsel) { - if (evsel->core.attr.config == TOPDOWN_SLOTS) - return true; - - return false; + return evsel->core.attr.type == PERF_TYPE_RAW && + evsel->core.attr.config == TOPDOWN_SLOTS && + evsel->core.attr.config1 == 0; } bool arch_is_topdown_metrics(const struct evsel *evsel) { - int config = evsel->core.attr.config; - const char *name_from_config; - struct perf_pmu *pmu; - - /* All topdown events have an event code of 0. */ - if ((config & 0xFF) != 0) - return false; - - pmu = evsel__find_pmu(evsel); - if (!pmu || !pmu->is_core) - return false; - - name_from_config = perf_pmu__name_from_config(pmu, config); - return name_from_config && strcasestr(name_from_config, "topdown"); + // cmask=0, inv=0, pc=0, edge=0, umask=0x80-0x87, event=0 + return evsel->core.attr.type == PERF_TYPE_RAW && + (evsel->core.attr.config & 0xFFFFF8FF) == 0x8000 && + evsel->core.attr.config1 == 0; } /* @@ -88,3 +77,31 @@ bool arch_topdown_sample_read(struct evsel *leader) return false; } + +/* + * Make a copy of the topdown metric event metric_event with the given index but + * change its configuration to be a topdown slots event. Copying from + * metric_event ensures modifiers are the same. + */ +int topdown_insert_slots_event(struct list_head *list, int idx, struct evsel *metric_event) +{ + struct evsel *evsel = evsel__new_idx(&metric_event->core.attr, idx); + + if (!evsel) + return -ENOMEM; + + evsel->core.attr.config = TOPDOWN_SLOTS; + evsel->core.cpus = perf_cpu_map__get(metric_event->core.cpus); + evsel->core.pmu_cpus = perf_cpu_map__get(metric_event->core.pmu_cpus); + evsel->core.is_pmu_core = true; + evsel->pmu = metric_event->pmu; + evsel->name = strdup("slots"); + evsel->precise_max = metric_event->precise_max; + evsel->sample_read = metric_event->sample_read; + evsel->weak_group = metric_event->weak_group; + evsel->bpf_counter = metric_event->bpf_counter; + evsel->retire_lat = metric_event->retire_lat; + evsel__set_leader(evsel, evsel__leader(metric_event)); + list_add_tail(&evsel->core.node, list); + return 0; +} diff --git a/tools/perf/arch/x86/util/topdown.h b/tools/perf/arch/x86/util/topdown.h index 1bae9b1822d7..69035565e649 100644 --- a/tools/perf/arch/x86/util/topdown.h +++ b/tools/perf/arch/x86/util/topdown.h @@ -2,8 +2,14 @@ #ifndef _TOPDOWN_H #define _TOPDOWN_H 1 +#include <stdbool.h> + +struct evsel; +struct list_head; + bool topdown_sys_has_perf_metrics(void); bool arch_is_topdown_slots(const struct evsel *evsel); bool arch_is_topdown_metrics(const struct evsel *evsel); +int topdown_insert_slots_event(struct list_head *list, int idx, struct evsel *metric_event); #endif |