diff options
Diffstat (limited to 'tools/perf/util/pmu.c')
-rw-r--r-- | tools/perf/util/pmu.c | 555 |
1 files changed, 87 insertions, 468 deletions
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index ad209c88a124..64fa568a5426 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -4,20 +4,15 @@ #include <linux/string.h> #include <linux/zalloc.h> #include <linux/ctype.h> -#include <subcmd/pager.h> #include <sys/types.h> -#include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <stdbool.h> -#include <stdarg.h> #include <dirent.h> #include <api/fs/fs.h> #include <locale.h> -#include <regex.h> -#include <perf/cpumap.h> #include <fnmatch.h> #include <math.h> #include "debug.h" @@ -32,7 +27,6 @@ #include "string2.h" #include "strbuf.h" #include "fncache.h" -#include "pmu-hybrid.h" #include "util/evsel_config.h" struct perf_pmu perf_pmu__fake; @@ -60,10 +54,6 @@ struct perf_pmu_format { struct list_head list; }; -static bool hybrid_scanned; - -static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name); - /* * Parse & process all the sysfs attributes located under * the directory specified in 'dir' parameter. @@ -557,36 +547,11 @@ static int pmu_alias_terms(struct perf_pmu_alias *alias, return 0; } -/* Add all pmus in sysfs to pmu list: */ -static void pmu_read_sysfs(void) -{ - int fd; - DIR *dir; - struct dirent *dent; - - fd = perf_pmu__event_source_devices_fd(); - if (fd < 0) - return; - - dir = fdopendir(fd); - if (!dir) - return; - - while ((dent = readdir(dir))) { - if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) - continue; - /* add to static LIST_HEAD(pmus): */ - perf_pmu__find2(fd, dent->d_name); - } - - closedir(dir); -} - /* * Uncore PMUs have a "cpumask" file under sysfs. CPU PMUs (e.g. on arm/arm64) * may have a "cpus" file. */ -static struct perf_cpu_map *pmu_cpumask(int dirfd, const char *name) +static struct perf_cpu_map *pmu_cpumask(int dirfd, const char *name, bool is_core) { struct perf_cpu_map *cpus; const char *templates[] = { @@ -610,16 +575,14 @@ static struct perf_cpu_map *pmu_cpumask(int dirfd, const char *name) return cpus; } - return NULL; + /* Nothing found, for core PMUs assume this means all CPUs. */ + return is_core ? perf_cpu_map__get(cpu_map__online()) : NULL; } static bool pmu_is_uncore(int dirfd, const char *name) { int fd; - if (perf_pmu__hybrid_mounted(name)) - return false; - fd = perf_pmu__pathname_fd(dirfd, name, "cpumask", O_PATH); if (fd < 0) return false; @@ -643,12 +606,14 @@ static char *pmu_id(const char *name) return str; } -/* - * PMU CORE devices have different name other than cpu in sysfs on some - * platforms. - * Looking for possible sysfs files to identify the arm core device. +/** + * is_sysfs_pmu_core() - PMU CORE devices have different name other than cpu in + * sysfs on some platforms like ARM or Intel hybrid. Looking for + * possible the cpus file in sysfs files to identify whether this is a + * core device. + * @name: The PMU name such as "cpu_atom". */ -static int is_arm_pmu_core(const char *name) +static int is_sysfs_pmu_core(const char *name) { char path[PATH_MAX]; @@ -777,9 +742,11 @@ out: } struct pmu_add_cpu_aliases_map_data { + /* List being added to. */ struct list_head *head; - const char *name; - const char *cpu_name; + /* If a pmu_event lacks a given PMU the default used. */ + char *default_pmu_name; + /* The PMU that we're searching for events for. */ struct perf_pmu *pmu; }; @@ -788,37 +755,32 @@ static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe, void *vdata) { struct pmu_add_cpu_aliases_map_data *data = vdata; - const char *pname = pe->pmu ? pe->pmu : data->cpu_name; - - if (data->pmu->is_uncore && pmu_uncore_alias_match(pname, data->name)) - goto new_alias; - - if (strcmp(pname, data->name)) - return 0; + const char *pname = pe->pmu ?: data->default_pmu_name; -new_alias: - /* need type casts to override 'const' */ - __perf_pmu__new_alias(data->head, -1, (char *)pe->name, (char *)pe->desc, - (char *)pe->event, pe); + if (!strcmp(pname, data->pmu->name) || + (data->pmu->is_uncore && pmu_uncore_alias_match(pname, data->pmu->name))) { + /* need type casts to override 'const' */ + __perf_pmu__new_alias(data->head, -1, (char *)pe->name, (char *)pe->desc, + (char *)pe->event, pe); + } return 0; } /* - * From the pmu_events_map, find the table of PMU events that corresponds - * to the current running CPU. Then, add all PMU events from that table - * as aliases. + * From the pmu_events_table, find the events that correspond to the given + * PMU and add them to the list 'head'. */ void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu, - const struct pmu_events_table *table) + const struct pmu_events_table *table) { struct pmu_add_cpu_aliases_map_data data = { .head = head, - .name = pmu->name, - .cpu_name = is_arm_pmu_core(pmu->name) ? pmu->name : "cpu", + .default_pmu_name = perf_pmus__default_pmu_name(), .pmu = pmu, }; pmu_events_table_for_each_event(table, pmu_add_cpu_aliases_map_callback, &data); + free(data.default_pmu_name); } static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu) @@ -898,23 +860,16 @@ static int pmu_max_precise(int dirfd, struct perf_pmu *pmu) return max_precise; } -static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name) +struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name) { struct perf_pmu *pmu; LIST_HEAD(format); LIST_HEAD(aliases); __u32 type; char *name = pmu_find_real_name(lookup_name); - bool is_hybrid = perf_pmu__hybrid_mounted(name); char *alias_name; /* - * Check pmu name for hybrid and the pmu may be invalid in sysfs - */ - if (!strncmp(name, "cpu_", 4) && !is_hybrid) - return NULL; - - /* * The pmu data we store & need consists of the pmu * type value and format definitions. Load both right * now. @@ -932,9 +887,9 @@ static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name) if (!pmu) return NULL; - pmu->cpus = pmu_cpumask(dirfd, name); + pmu->is_core = is_pmu_core(name); + pmu->cpus = pmu_cpumask(dirfd, name, pmu->is_core); pmu->name = strdup(name); - if (!pmu->name) goto err; @@ -962,12 +917,7 @@ static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name) INIT_LIST_HEAD(&pmu->caps); list_splice(&format, &pmu->format); list_splice(&aliases, &pmu->aliases); - list_add_tail(&pmu->list, &pmus); - - if (is_hybrid) - list_add_tail(&pmu->hybrid_list, &perf_pmu__hybrid_pmus); - else - INIT_LIST_HEAD(&pmu->hybrid_list); + list_add_tail(&pmu->list, pmus); pmu->default_config = perf_pmu__get_default_config(pmu); @@ -982,6 +932,11 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) { struct perf_pmu_format *format; + if (pmu->formats_checked) + return; + + pmu->formats_checked = true; + /* fake pmu doesn't have format list */ if (pmu == &perf_pmu__fake) return; @@ -995,61 +950,6 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) } } -static struct perf_pmu *pmu_find(const char *name) -{ - struct perf_pmu *pmu; - - list_for_each_entry(pmu, &pmus, list) { - if (!strcmp(pmu->name, name) || - (pmu->alias_name && !strcmp(pmu->alias_name, name))) - return pmu; - } - - return NULL; -} - -struct perf_pmu *perf_pmu__find_by_type(unsigned int type) -{ - struct perf_pmu *pmu; - - list_for_each_entry(pmu, &pmus, list) - if (pmu->type == type) - return pmu; - - return NULL; -} - -struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu) -{ - /* - * pmu iterator: If pmu is NULL, we start at the begin, - * otherwise return the next pmu. Returns NULL on end. - */ - if (!pmu) { - pmu_read_sysfs(); - pmu = list_prepare_entry(pmu, &pmus, list); - } - list_for_each_entry_continue(pmu, &pmus, list) - return pmu; - return NULL; -} - -struct perf_pmu *evsel__find_pmu(const struct evsel *evsel) -{ - struct perf_pmu *pmu = NULL; - - if (evsel->pmu) - return evsel->pmu; - - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - if (pmu->type == evsel->core.attr.type) - break; - } - - ((struct evsel *)evsel)->pmu = pmu; - return pmu; -} - bool evsel__is_aux_event(const struct evsel *evsel) { struct perf_pmu *pmu = evsel__find_pmu(evsel); @@ -1086,43 +986,6 @@ void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel, evsel->core.attr.config |= field_prep(bits, val); } -struct perf_pmu *perf_pmu__find(const char *name) -{ - struct perf_pmu *pmu; - int dirfd; - - /* - * Once PMU is loaded it stays in the list, - * so we keep us from multiple reading/parsing - * the pmu format definitions. - */ - pmu = pmu_find(name); - if (pmu) - return pmu; - - dirfd = perf_pmu__event_source_devices_fd(); - pmu = pmu_lookup(dirfd, name); - close(dirfd); - - return pmu; -} - -static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name) -{ - struct perf_pmu *pmu; - - /* - * Once PMU is loaded it stays in the list, - * so we keep us from multiple reading/parsing - * the pmu format definitions. - */ - pmu = pmu_find(name); - if (pmu) - return pmu; - - return pmu_lookup(dirfd, name); -} - static struct perf_pmu_format * pmu_find_format(struct list_head *formats, const char *name) { @@ -1398,7 +1261,6 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, { bool zero = !!pmu->default_config; - attr->type = pmu->type; return perf_pmu__config_terms(pmu->name, &pmu->format, attr, head_terms, zero, err); } @@ -1553,250 +1415,46 @@ void perf_pmu__del_formats(struct list_head *formats) } } -static int sub_non_neg(int a, int b) -{ - if (b > a) - return 0; - return a - b; -} - -static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, - const struct perf_pmu_alias *alias) -{ - struct parse_events_term *term; - int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name); - - list_for_each_entry(term, &alias->terms, list) { - if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) - used += snprintf(buf + used, sub_non_neg(len, used), - ",%s=%s", term->config, - term->val.str); - } - - if (sub_non_neg(len, used) > 0) { - buf[used] = '/'; - used++; - } - if (sub_non_neg(len, used) > 0) { - buf[used] = '\0'; - used++; - } else - buf[len - 1] = '\0'; - - return buf; -} - -/** Struct for ordering events as output in perf list. */ -struct sevent { - /** PMU for event. */ - const struct perf_pmu *pmu; - /** - * Optional event for name, desc, etc. If not present then this is a - * selectable PMU and the event name is shown as "//". - */ - const struct perf_pmu_alias *event; - /** Is the PMU for the CPU? */ - bool is_cpu; -}; - -static int cmp_sevent(const void *a, const void *b) +bool is_pmu_core(const char *name) { - const struct sevent *as = a; - const struct sevent *bs = b; - const char *a_pmu_name = NULL, *b_pmu_name = NULL; - const char *a_name = "//", *a_desc = NULL, *a_topic = ""; - const char *b_name = "//", *b_desc = NULL, *b_topic = ""; - int ret; - - if (as->event) { - a_name = as->event->name; - a_desc = as->event->desc; - a_topic = as->event->topic ?: ""; - a_pmu_name = as->event->pmu_name; - } - if (bs->event) { - b_name = bs->event->name; - b_desc = bs->event->desc; - b_topic = bs->event->topic ?: ""; - b_pmu_name = bs->event->pmu_name; - } - /* Put extra events last. */ - if (!!a_desc != !!b_desc) - return !!a_desc - !!b_desc; - - /* Order by topics. */ - ret = strcmp(a_topic, b_topic); - if (ret) - return ret; - - /* Order CPU core events to be first */ - if (as->is_cpu != bs->is_cpu) - return as->is_cpu ? -1 : 1; - - /* Order by PMU name. */ - if (as->pmu != bs->pmu) { - a_pmu_name = a_pmu_name ?: (as->pmu->name ?: ""); - b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: ""); - ret = strcmp(a_pmu_name, b_pmu_name); - if (ret) - return ret; - } - - /* Order by event name. */ - return strcmp(a_name, b_name); + return !strcmp(name, "cpu") || !strcmp(name, "cpum_cf") || is_sysfs_pmu_core(name); } -bool is_pmu_core(const char *name) +bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu) { - return !strcmp(name, "cpu") || is_arm_pmu_core(name); + return pmu->is_core; } -static bool pmu_alias_is_duplicate(struct sevent *alias_a, - struct sevent *alias_b) +bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu) { - const char *a_pmu_name = NULL, *b_pmu_name = NULL; - const char *a_name = "//", *b_name = "//"; - - - if (alias_a->event) { - a_name = alias_a->event->name; - a_pmu_name = alias_a->event->pmu_name; - } - if (alias_b->event) { - b_name = alias_b->event->name; - b_pmu_name = alias_b->event->pmu_name; - } - - /* Different names -> never duplicates */ - if (strcmp(a_name, b_name)) - return false; - - /* Don't remove duplicates for different PMUs */ - a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: ""); - b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: ""); - return strcmp(a_pmu_name, b_pmu_name) == 0; + return pmu->is_core && perf_pmus__num_core_pmus() == 1; } -void print_pmu_events(const struct print_callbacks *print_cb, void *print_state) +bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name) { - struct perf_pmu *pmu; - struct perf_pmu_alias *event; - char buf[1024]; - int printed = 0; - int len, j; - struct sevent *aliases; - - pmu = NULL; - len = 0; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - list_for_each_entry(event, &pmu->aliases, list) - len++; - if (pmu->selectable) - len++; - } - aliases = zalloc(sizeof(struct sevent) * len); - if (!aliases) { - pr_err("FATAL: not enough memory to print PMU events\n"); - return; - } - pmu = NULL; - j = 0; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - bool is_cpu = is_pmu_core(pmu->name) || perf_pmu__is_hybrid(pmu->name); - - list_for_each_entry(event, &pmu->aliases, list) { - aliases[j].event = event; - aliases[j].pmu = pmu; - aliases[j].is_cpu = is_cpu; - j++; - } - if (pmu->selectable) { - aliases[j].event = NULL; - aliases[j].pmu = pmu; - aliases[j].is_cpu = is_cpu; - j++; - } - } - len = j; - qsort(aliases, len, sizeof(struct sevent), cmp_sevent); - for (j = 0; j < len; j++) { - const char *name, *alias = NULL, *scale_unit = NULL, - *desc = NULL, *long_desc = NULL, - *encoding_desc = NULL, *topic = NULL, - *pmu_name = NULL; - bool deprecated = false; - size_t buf_used; - - /* Skip duplicates */ - if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1])) - continue; + struct perf_pmu_alias *alias; - if (!aliases[j].event) { - /* A selectable event. */ - pmu_name = aliases[j].pmu->name; - buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1; - name = buf; - } else { - if (aliases[j].event->desc) { - name = aliases[j].event->name; - buf_used = 0; - } else { - name = format_alias(buf, sizeof(buf), aliases[j].pmu, - aliases[j].event); - if (aliases[j].is_cpu) { - alias = name; - name = aliases[j].event->name; - } - buf_used = strlen(buf) + 1; - } - pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: ""); - if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) { - scale_unit = buf + buf_used; - buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, - "%G%s", aliases[j].event->scale, - aliases[j].event->unit) + 1; - } - desc = aliases[j].event->desc; - long_desc = aliases[j].event->long_desc; - topic = aliases[j].event->topic; - encoding_desc = buf + buf_used; - buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, - "%s/%s/", pmu_name, aliases[j].event->str) + 1; - deprecated = aliases[j].event->deprecated; - } - print_cb->print_event(print_state, - pmu_name, - topic, - name, - alias, - scale_unit, - deprecated, - "Kernel PMU event", - desc, - long_desc, - encoding_desc); + list_for_each_entry(alias, &pmu->aliases, list) { + if (!strcmp(alias->name, name)) + return true; } - if (printed && pager_in_use()) - printf("\n"); - - zfree(&aliases); - return; + return false; } -bool pmu_have_event(const char *pname, const char *name) +bool perf_pmu__is_software(const struct perf_pmu *pmu) { - struct perf_pmu *pmu; - struct perf_pmu_alias *alias; - - pmu = NULL; - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - if (strcmp(pname, pmu->name)) - continue; - list_for_each_entry(alias, &pmu->aliases, list) - if (!strcmp(alias->name, name)) - return true; + if (pmu->is_core || pmu->is_uncore || pmu->auxtrace) + return false; + switch (pmu->type) { + case PERF_TYPE_HARDWARE: return false; + case PERF_TYPE_SOFTWARE: return true; + case PERF_TYPE_TRACEPOINT: return true; + case PERF_TYPE_HW_CACHE: return false; + case PERF_TYPE_RAW: return false; + case PERF_TYPE_BREAKPOINT: return true; + default: break; } - return false; + return !strcmp(pmu->name, "kprobe") || !strcmp(pmu->name, "uprobe"); } FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) @@ -1967,47 +1625,53 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu) return pmu->nr_caps; } -void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, - const char *name) +static void perf_pmu__compute_config_masks(struct perf_pmu *pmu) { struct perf_pmu_format *format; - __u64 masks = 0, bits; - char buf[100]; - unsigned int i; + + if (pmu->config_masks_computed) + return; list_for_each_entry(format, &pmu->format, list) { - if (format->value != PERF_PMU_FORMAT_VALUE_CONFIG) + unsigned int i; + __u64 *mask; + + if (format->value >= PERF_PMU_FORMAT_VALUE_CONFIG_END) continue; + pmu->config_masks_present = true; + mask = &pmu->config_masks[format->value]; + for_each_set_bit(i, format->bits, PERF_PMU_FORMAT_BITS) - masks |= 1ULL << i; + *mask |= 1ULL << i; } + pmu->config_masks_computed = true; +} + +void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, + const char *name, int config_num, + const char *config_name) +{ + __u64 bits; + char buf[100]; + + perf_pmu__compute_config_masks(pmu); /* * Kernel doesn't export any valid format bits. */ - if (masks == 0) + if (!pmu->config_masks_present) return; - bits = config & ~masks; + bits = config & ~pmu->config_masks[config_num]; if (bits == 0) return; bitmap_scnprintf((unsigned long *)&bits, sizeof(bits) * 8, buf, sizeof(buf)); - pr_warning("WARNING: event '%s' not valid (bits %s of config " + pr_warning("WARNING: event '%s' not valid (bits %s of %s " "'%llx' not supported by kernel)!\n", - name ?: "N/A", buf, config); -} - -bool perf_pmu__has_hybrid(void) -{ - if (!hybrid_scanned) { - hybrid_scanned = true; - perf_pmu__scan(NULL); - } - - return !list_empty(&perf_pmu__hybrid_pmus); + name ?: "N/A", buf, config_name, config); } int perf_pmu__match(char *pattern, char *name, char *tok) @@ -2024,39 +1688,6 @@ int perf_pmu__match(char *pattern, char *name, char *tok) return 0; } -int perf_pmu__cpus_match(struct perf_pmu *pmu, struct perf_cpu_map *cpus, - struct perf_cpu_map **mcpus_ptr, - struct perf_cpu_map **ucpus_ptr) -{ - struct perf_cpu_map *pmu_cpus = pmu->cpus; - struct perf_cpu_map *matched_cpus, *unmatched_cpus; - struct perf_cpu cpu; - int i, matched_nr = 0, unmatched_nr = 0; - - matched_cpus = perf_cpu_map__default_new(); - if (!matched_cpus) - return -1; - - unmatched_cpus = perf_cpu_map__default_new(); - if (!unmatched_cpus) { - perf_cpu_map__put(matched_cpus); - return -1; - } - - perf_cpu_map__for_each_cpu(cpu, i, cpus) { - if (!perf_cpu_map__has(pmu_cpus, cpu)) - RC_CHK_ACCESS(unmatched_cpus)->map[unmatched_nr++] = cpu; - else - RC_CHK_ACCESS(matched_cpus)->map[matched_nr++] = cpu; - } - - perf_cpu_map__set_nr(unmatched_cpus, unmatched_nr); - perf_cpu_map__set_nr(matched_cpus, matched_nr); - *mcpus_ptr = matched_cpus; - *ucpus_ptr = unmatched_cpus; - return 0; -} - double __weak perf_pmu__cpu_slots_per_cycle(void) { return NAN; @@ -2110,7 +1741,7 @@ int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, return openat(dirfd, path, flags); } -static void perf_pmu__delete(struct perf_pmu *pmu) +void perf_pmu__delete(struct perf_pmu *pmu) { perf_pmu__del_formats(&pmu->format); perf_pmu__del_aliases(pmu); @@ -2123,15 +1754,3 @@ static void perf_pmu__delete(struct perf_pmu *pmu) zfree(&pmu->alias_name); free(pmu); } - -void perf_pmu__destroy(void) -{ - struct perf_pmu *pmu, *tmp; - - list_for_each_entry_safe(pmu, tmp, &pmus, list) { - list_del(&pmu->list); - list_del(&pmu->hybrid_list); - - perf_pmu__delete(pmu); - } -} |