| // SPDX-License-Identifier: GPL-2.0-only |
| #include <errno.h> |
| #include <inttypes.h> |
| #include "cpumap.h" |
| #include "evlist.h" |
| #include "evsel.h" |
| #include "../perf.h" |
| #include "util/pmu-hybrid.h" |
| #include "util/evlist-hybrid.h" |
| #include "debug.h" |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <linux/err.h> |
| #include <linux/string.h> |
| #include <perf/evlist.h> |
| #include <perf/evsel.h> |
| #include <perf/cpumap.h> |
| |
| int evlist__add_default_hybrid(struct evlist *evlist, bool precise) |
| { |
| struct evsel *evsel; |
| struct perf_pmu *pmu; |
| __u64 config; |
| struct perf_cpu_map *cpus; |
| |
| perf_pmu__for_each_hybrid_pmu(pmu) { |
| config = PERF_COUNT_HW_CPU_CYCLES | |
| ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT); |
| evsel = evsel__new_cycles(precise, PERF_TYPE_HARDWARE, |
| config); |
| if (!evsel) |
| return -ENOMEM; |
| |
| cpus = perf_cpu_map__get(pmu->cpus); |
| evsel->core.cpus = cpus; |
| evsel->core.own_cpus = perf_cpu_map__get(cpus); |
| evsel->pmu_name = strdup(pmu->name); |
| evlist__add(evlist, evsel); |
| } |
| |
| return 0; |
| } |
| |
| static bool group_hybrid_conflict(struct evsel *leader) |
| { |
| struct evsel *pos, *prev = NULL; |
| |
| for_each_group_evsel(pos, leader) { |
| if (!evsel__is_hybrid(pos)) |
| continue; |
| |
| if (prev && strcmp(prev->pmu_name, pos->pmu_name)) |
| return true; |
| |
| prev = pos; |
| } |
| |
| return false; |
| } |
| |
| void evlist__warn_hybrid_group(struct evlist *evlist) |
| { |
| struct evsel *evsel; |
| |
| evlist__for_each_entry(evlist, evsel) { |
| if (evsel__is_group_leader(evsel) && |
| evsel->core.nr_members > 1 && |
| group_hybrid_conflict(evsel)) { |
| pr_warning("WARNING: events in group from " |
| "different hybrid PMUs!\n"); |
| return; |
| } |
| } |
| } |
| |
| bool evlist__has_hybrid(struct evlist *evlist) |
| { |
| struct evsel *evsel; |
| |
| evlist__for_each_entry(evlist, evsel) { |
| if (evsel->pmu_name && |
| perf_pmu__is_hybrid(evsel->pmu_name)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| int evlist__fix_hybrid_cpus(struct evlist *evlist, const char *cpu_list) |
| { |
| struct perf_cpu_map *cpus; |
| struct evsel *evsel, *tmp; |
| struct perf_pmu *pmu; |
| int ret, unmatched_count = 0, events_nr = 0; |
| |
| if (!perf_pmu__has_hybrid() || !cpu_list) |
| return 0; |
| |
| cpus = perf_cpu_map__new(cpu_list); |
| if (!cpus) |
| return -1; |
| |
| /* |
| * The evsels are created with hybrid pmu's cpus. But now we |
| * need to check and adjust the cpus of evsel by cpu_list because |
| * cpu_list may cause conflicts with cpus of evsel. For example, |
| * cpus of evsel is cpu0-7, but the cpu_list is cpu6-8, we need |
| * to adjust the cpus of evsel to cpu6-7. And then propatate maps |
| * in evlist__create_maps(). |
| */ |
| evlist__for_each_entry_safe(evlist, tmp, evsel) { |
| struct perf_cpu_map *matched_cpus, *unmatched_cpus; |
| char buf1[128], buf2[128]; |
| |
| pmu = perf_pmu__find_hybrid_pmu(evsel->pmu_name); |
| if (!pmu) |
| continue; |
| |
| ret = perf_pmu__cpus_match(pmu, cpus, &matched_cpus, |
| &unmatched_cpus); |
| if (ret) |
| goto out; |
| |
| events_nr++; |
| |
| if (perf_cpu_map__nr(matched_cpus) > 0 && |
| (perf_cpu_map__nr(unmatched_cpus) > 0 || |
| perf_cpu_map__nr(matched_cpus) < perf_cpu_map__nr(cpus) || |
| perf_cpu_map__nr(matched_cpus) < perf_cpu_map__nr(pmu->cpus))) { |
| perf_cpu_map__put(evsel->core.cpus); |
| perf_cpu_map__put(evsel->core.own_cpus); |
| evsel->core.cpus = perf_cpu_map__get(matched_cpus); |
| evsel->core.own_cpus = perf_cpu_map__get(matched_cpus); |
| |
| if (perf_cpu_map__nr(unmatched_cpus) > 0) { |
| cpu_map__snprint(matched_cpus, buf1, sizeof(buf1)); |
| pr_warning("WARNING: use %s in '%s' for '%s', skip other cpus in list.\n", |
| buf1, pmu->name, evsel->name); |
| } |
| } |
| |
| if (perf_cpu_map__nr(matched_cpus) == 0) { |
| evlist__remove(evlist, evsel); |
| evsel__delete(evsel); |
| |
| cpu_map__snprint(cpus, buf1, sizeof(buf1)); |
| cpu_map__snprint(pmu->cpus, buf2, sizeof(buf2)); |
| pr_warning("WARNING: %s isn't a '%s', please use a CPU list in the '%s' range (%s)\n", |
| buf1, pmu->name, pmu->name, buf2); |
| unmatched_count++; |
| } |
| |
| perf_cpu_map__put(matched_cpus); |
| perf_cpu_map__put(unmatched_cpus); |
| } |
| |
| ret = (unmatched_count == events_nr) ? -1 : 0; |
| out: |
| perf_cpu_map__put(cpus); |
| return ret; |
| } |