diff options
Diffstat (limited to 'tools/perf/util/pmu.c')
-rw-r--r-- | tools/perf/util/pmu.c | 411 |
1 files changed, 8 insertions, 403 deletions
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 21ee23b78f5a..05056305fb58 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" @@ -59,8 +54,6 @@ struct perf_pmu_format { struct list_head list; }; -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. @@ -554,31 +547,6 @@ 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. @@ -894,7 +862,7 @@ 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); @@ -951,7 +919,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); + list_add_tail(&pmu->list, pmus); pmu->default_config = perf_pmu__get_default_config(pmu); @@ -979,61 +947,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); @@ -1070,43 +983,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) { @@ -1536,99 +1412,6 @@ 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) -{ - 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); -} - bool is_pmu_core(const char *name) { return !strcmp(name, "cpu") || is_sysfs_pmu_core(name); @@ -1654,167 +1437,18 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu) return !is_pmu_hybrid(pmu->name); } -static bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu) +bool perf_pmu__is_mem_pmu(const struct perf_pmu *pmu) { return pmu->is_core; } -int perf_pmu__num_mem_pmus(void) -{ - struct perf_pmu *pmu = NULL; - int count = 0; - - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - if (perf_pmu__is_mem_pmu(pmu)) - count++; - } - return count; -} - -static bool pmu_alias_is_duplicate(struct sevent *alias_a, - struct sevent *alias_b) -{ - 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; -} - -void print_pmu_events(const struct print_callbacks *print_cb, void *print_state) -{ - 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 = pmu->is_core; - - 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; - - 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); - } - if (printed && pager_in_use()) - printf("\n"); - - zfree(&aliases); - return; -} - -bool pmu_have_event(const char *pname, const char *name) +bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name) { - 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; + list_for_each_entry(alias, &pmu->aliases, list) { + if (!strcmp(alias->name, name)) + return true; } return false; } @@ -2020,24 +1654,6 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, name ?: "N/A", buf, config); } -bool perf_pmu__has_hybrid(void) -{ - static bool hybrid_scanned, has_hybrid; - - if (!hybrid_scanned) { - struct perf_pmu *pmu = NULL; - - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - if (pmu->is_core && is_pmu_hybrid(pmu->name)) { - has_hybrid = true; - break; - } - } - hybrid_scanned = true; - } - return has_hybrid; -} - int perf_pmu__match(char *pattern, char *name, char *tok) { if (!name) @@ -2105,7 +1721,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); @@ -2118,14 +1734,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); - - perf_pmu__delete(pmu); - } -} |