diff options
Diffstat (limited to 'tools/perf/util/pmu.c')
-rw-r--r-- | tools/perf/util/pmu.c | 300 |
1 files changed, 244 insertions, 56 deletions
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index ddb0261b2577..49bfee0e3d9e 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -12,6 +12,9 @@ #include "pmu.h" #include "parse-events.h" #include "cpumap.h" +#include "header.h" +#include "pmu-events/pmu-events.h" +#include "cache.h" struct perf_pmu_format { char *name; @@ -91,32 +94,10 @@ static int pmu_format(const char *name, struct list_head *format) return 0; } -static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) +static int convert_scale(const char *scale, char **end, double *sval) { - struct stat st; - ssize_t sret; - char scale[128]; - int fd, ret = -1; - char path[PATH_MAX]; char *lc; - - snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); - - fd = open(path, O_RDONLY); - if (fd == -1) - return -1; - - if (fstat(fd, &st) < 0) - goto error; - - sret = read(fd, scale, sizeof(scale)-1); - if (sret < 0) - goto error; - - if (scale[sret - 1] == '\n') - scale[sret - 1] = '\0'; - else - scale[sret] = '\0'; + int ret = 0; /* * save current locale @@ -131,7 +112,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * lc = strdup(lc); if (!lc) { ret = -ENOMEM; - goto error; + goto out; } /* @@ -141,14 +122,42 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * */ setlocale(LC_NUMERIC, "C"); - alias->scale = strtod(scale, NULL); + *sval = strtod(scale, end); +out: /* restore locale */ setlocale(LC_NUMERIC, lc); - free(lc); + return ret; +} + +static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) +{ + struct stat st; + ssize_t sret; + char scale[128]; + int fd, ret = -1; + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + if (fstat(fd, &st) < 0) + goto error; + + sret = read(fd, scale, sizeof(scale)-1); + if (sret < 0) + goto error; - ret = 0; + if (scale[sret - 1] == '\n') + scale[sret - 1] = '\0'; + else + scale[sret] = '\0'; + + ret = convert_scale(scale, NULL, &alias->scale); error: close(fd); return ret; @@ -220,10 +229,13 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, } static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, - char *desc __maybe_unused, char *val) + char *desc, char *val, + char *long_desc, char *topic, + char *unit, char *perpkg) { struct perf_pmu_alias *alias; int ret; + int num; alias = malloc(sizeof(*alias)); if (!alias) @@ -253,6 +265,18 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, perf_pmu__parse_snapshot(alias, dir, name); } + alias->desc = desc ? strdup(desc) : NULL; + alias->long_desc = long_desc ? strdup(long_desc) : + desc ? strdup(desc) : NULL; + alias->topic = topic ? strdup(topic) : NULL; + if (unit) { + if (convert_scale(unit, &unit, &alias->scale) < 0) + return -1; + snprintf(alias->unit, sizeof(alias->unit), "%s", unit); + } + alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1; + alias->str = strdup(val); + list_add_tail(&alias->list, list); return 0; @@ -269,7 +293,8 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI buf[ret] = 0; - return __perf_pmu__new_alias(list, dir, name, NULL, buf); + return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL, + NULL); } static inline bool pmu_alias_info_file(char *name) @@ -445,14 +470,23 @@ static struct cpu_map *pmu_cpumask(const char *name) FILE *file; struct cpu_map *cpus; const char *sysfs = sysfs__mountpoint(); + const char *templates[] = { + "%s/bus/event_source/devices/%s/cpumask", + "%s/bus/event_source/devices/%s/cpus", + NULL + }; + const char **template; if (!sysfs) return NULL; - snprintf(path, PATH_MAX, - "%s/bus/event_source/devices/%s/cpumask", sysfs, name); + for (template = templates; *template; template++) { + snprintf(path, PATH_MAX, *template, sysfs, name); + if (stat(path, &st) == 0) + break; + } - if (stat(path, &st) < 0) + if (!*template) return NULL; file = fopen(path, "r"); @@ -464,6 +498,79 @@ static struct cpu_map *pmu_cpumask(const char *name) return cpus; } +/* + * Return the CPU id as a raw string. + * + * Each architecture should provide a more precise id string that + * can be use to match the architecture's "mapfile". + */ +char * __weak get_cpuid_str(void) +{ + return NULL; +} + +/* + * 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. + */ +static void pmu_add_cpu_aliases(struct list_head *head, const char *name) +{ + int i; + struct pmu_events_map *map; + struct pmu_event *pe; + char *cpuid; + static bool printed; + + cpuid = getenv("PERF_CPUID"); + if (cpuid) + cpuid = strdup(cpuid); + if (!cpuid) + cpuid = get_cpuid_str(); + if (!cpuid) + return; + + if (!printed) { + pr_debug("Using CPUID %s\n", cpuid); + printed = true; + } + + i = 0; + while (1) { + map = &pmu_events_map[i++]; + if (!map->table) + goto out; + + if (!strcmp(map->cpuid, cpuid)) + break; + } + + /* + * Found a matching PMU events table. Create aliases + */ + i = 0; + while (1) { + const char *pname; + + pe = &map->table[i++]; + if (!pe->name) + break; + + pname = pe->pmu ? pe->pmu : "cpu"; + if (strncmp(pname, name, strlen(pname))) + continue; + + /* need type casts to override 'const' */ + __perf_pmu__new_alias(head, NULL, (char *)pe->name, + (char *)pe->desc, (char *)pe->event, + (char *)pe->long_desc, (char *)pe->topic, + (char *)pe->unit, (char *)pe->perpkg); + } + +out: + free(cpuid); +} + struct perf_event_attr * __weak perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) { @@ -485,12 +592,16 @@ static struct perf_pmu *pmu_lookup(const char *name) if (pmu_format(name, &format)) return NULL; - if (pmu_aliases(name, &aliases)) + /* + * Check the type first to avoid unnecessary work. + */ + if (pmu_type(name, &type)) return NULL; - if (pmu_type(name, &type)) + if (pmu_aliases(name, &aliases)) return NULL; + pmu_add_cpu_aliases(&aliases, name); pmu = zalloc(sizeof(*pmu)); if (!pmu) return NULL; @@ -834,12 +945,12 @@ static int check_info_data(struct perf_pmu_alias *alias, * define unit, scale and snapshot, fail * if there's more than one. */ - if ((info->unit && alias->unit) || + if ((info->unit && alias->unit[0]) || (info->scale && alias->scale) || (info->snapshot && alias->snapshot)) return -EINVAL; - if (alias->unit) + if (alias->unit[0]) info->unit = alias->unit; if (alias->scale) @@ -974,21 +1085,65 @@ static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu, return buf; } -static int cmp_string(const void *a, const void *b) +struct sevent { + char *name; + char *desc; + char *topic; + char *str; + char *pmu; +}; + +static int cmp_sevent(const void *a, const void *b) +{ + const struct sevent *as = a; + const struct sevent *bs = b; + + /* Put extra events last */ + if (!!as->desc != !!bs->desc) + return !!as->desc - !!bs->desc; + if (as->topic && bs->topic) { + int n = strcmp(as->topic, bs->topic); + + if (n) + return n; + } + return strcmp(as->name, bs->name); +} + +static void wordwrap(char *s, int start, int max, int corr) { - const char * const *as = a; - const char * const *bs = b; - return strcmp(*as, *bs); + int column = start; + int n; + + while (*s) { + int wlen = strcspn(s, " \t"); + + if (column + wlen >= max && column > start) { + printf("\n%*s", start, ""); + column = start + corr; + } + n = printf("%s%.*s", column > start ? " " : "", wlen, s); + if (n <= 0) + break; + s += wlen; + column += n; + while (isspace(*s)) + s++; + } } -void print_pmu_events(const char *event_glob, bool name_only) +void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, + bool long_desc) { struct perf_pmu *pmu; struct perf_pmu_alias *alias; char buf[1024]; int printed = 0; int len, j; - char **aliases; + struct sevent *aliases; + int numdesc = 0; + int columns = pager_get_columns(); + char *topic = NULL; pmu = NULL; len = 0; @@ -998,28 +1153,42 @@ void print_pmu_events(const char *event_glob, bool name_only) if (pmu->selectable) len++; } - aliases = zalloc(sizeof(char *) * len); + aliases = zalloc(sizeof(struct sevent) * len); if (!aliases) goto out_enomem; pmu = NULL; j = 0; while ((pmu = perf_pmu__scan(pmu)) != NULL) { list_for_each_entry(alias, &pmu->aliases, list) { - char *name = format_alias(buf, sizeof(buf), pmu, alias); + char *name = alias->desc ? alias->name : + format_alias(buf, sizeof(buf), pmu, alias); bool is_cpu = !strcmp(pmu->name, "cpu"); if (event_glob != NULL && - !(strglobmatch(name, event_glob) || - (!is_cpu && strglobmatch(alias->name, - event_glob)))) + !(strglobmatch_nocase(name, event_glob) || + (!is_cpu && strglobmatch_nocase(alias->name, + event_glob)) || + (alias->topic && + strglobmatch_nocase(alias->topic, event_glob)))) continue; - if (is_cpu && !name_only) + if (is_cpu && !name_only && !alias->desc) name = format_alias_or(buf, sizeof(buf), pmu, alias); - aliases[j] = strdup(name); - if (aliases[j] == NULL) + aliases[j].name = name; + if (is_cpu && !name_only && !alias->desc) + aliases[j].name = format_alias_or(buf, + sizeof(buf), + pmu, alias); + aliases[j].name = strdup(aliases[j].name); + if (!aliases[j].name) goto out_enomem; + + aliases[j].desc = long_desc ? alias->long_desc : + alias->desc; + aliases[j].topic = alias->topic; + aliases[j].str = alias->str; + aliases[j].pmu = pmu->name; j++; } if (pmu->selectable && @@ -1027,25 +1196,44 @@ void print_pmu_events(const char *event_glob, bool name_only) char *s; if (asprintf(&s, "%s//", pmu->name) < 0) goto out_enomem; - aliases[j] = s; + aliases[j].name = s; j++; } } len = j; - qsort(aliases, len, sizeof(char *), cmp_string); + qsort(aliases, len, sizeof(struct sevent), cmp_sevent); for (j = 0; j < len; j++) { + /* Skip duplicates */ + if (j > 0 && !strcmp(aliases[j].name, aliases[j - 1].name)) + continue; if (name_only) { - printf("%s ", aliases[j]); + printf("%s ", aliases[j].name); continue; } - printf(" %-50s [Kernel PMU event]\n", aliases[j]); + if (aliases[j].desc && !quiet_flag) { + if (numdesc++ == 0) + printf("\n"); + if (aliases[j].topic && (!topic || + strcmp(topic, aliases[j].topic))) { + printf("%s%s:\n", topic ? "\n" : "", + aliases[j].topic); + topic = aliases[j].topic; + } + printf(" %-50s\n", aliases[j].name); + printf("%*s", 8, "["); + wordwrap(aliases[j].desc, 8, columns, 0); + printf("]\n"); + if (verbose) + printf("%*s%s/%s/\n", 8, "", aliases[j].pmu, aliases[j].str); + } else + printf(" %-50s [Kernel PMU event]\n", aliases[j].name); printed++; } if (printed && pager_in_use()) printf("\n"); out_free: for (j = 0; j < len; j++) - zfree(&aliases[j]); + zfree(&aliases[j].name); zfree(&aliases); return; |