summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/pmu.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2016-10-04 10:04:47 +0200
committerIngo Molnar <mingo@kernel.org>2016-10-04 10:04:47 +0200
commit8657355f5b5f657407efc12a2223e8a3a6d658de (patch)
tree29608a0c914ce6f8fc67a90bb1df282eac58a20a /tools/perf/util/pmu.c
parent597f03f9d133e9837d00965016170271d4f87dcf (diff)
parentb42c7369e3f451e22c2b0be5d193955498d37546 (diff)
downloadlinux-8657355f5b5f657407efc12a2223e8a3a6d658de.tar.bz2
Merge tag 'perf-core-for-mingo-20161003' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent
Pull perf/core improvements and fixes: - Allow vendors to provide JSON files describing PMU events, that then get parsed to generate C tables that are linked against perf, allowing the use of the names in their documentations, such as: # perf list l1d List of pre-defined events (to be used in -e): Cache: l1d.replacement [L1D data line replacements] l1d_pend_miss.fb_full [Cycles a demand request was blocked due to Fill Buffers inavailability] l1d_pend_miss.pending [L1D miss oustandings duration in cycles] l1d_pend_miss.pending_cycles [Cycles with L1D load Misses outstanding] l1d_pend_miss.pending_cycles_any [Cycles with L1D load Misses outstanding from any thread on physical core] l2_trans.l1d_wb [L1D writebacks that access L2 cache] Pipeline: cycle_activity.cycles_l1d_miss [Cycles while L1 cache miss demand load is outstanding] cycle_activity.cycles_l1d_pending [Cycles while L1 cache miss demand load is outstanding] cycle_activity.stalls_l1d_miss [Execution stalls while L1 cache miss demand load is outstanding] cycle_activity.stalls_l1d_pending [Execution stalls while L1 cache miss demand load is outstanding] The above example was done on a Broadwell based ThinkPad t450s after downloading and installing such JSON files which will be added to the tools/perf/pmu-events/ directory in a subsequent patchkit. Now one can use those names with -e/--event in all 'perf tools'. (Andi Kleen, Sukadev Bhattiprolu) - Add a missing pointer dereference in 'perf probe' (Colin Ian King) - Add support for building host programs to be used in generating files to be used in the build process, such as fixdep and jevents, fixing the usage of these features in a cross compilation setup (Jiri Olsa) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf/util/pmu.c')
-rw-r--r--tools/perf/util/pmu.c176
1 files changed, 158 insertions, 18 deletions
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 2babcdf62839..b1474dcadfa2 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;
@@ -220,7 +223,8 @@ 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)
{
struct perf_pmu_alias *alias;
int ret;
@@ -253,6 +257,11 @@ 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;
+
list_add_tail(&alias->list, list);
return 0;
@@ -269,7 +278,7 @@ 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);
}
static inline bool pmu_alias_info_file(char *name)
@@ -473,6 +482,68 @@ 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)
+{
+ int i;
+ struct pmu_events_map *map;
+ struct pmu_event *pe;
+ char *cpuid;
+
+ cpuid = getenv("PERF_CPUID");
+ if (cpuid)
+ cpuid = strdup(cpuid);
+ if (!cpuid)
+ cpuid = get_cpuid_str();
+ if (!cpuid)
+ return;
+
+ pr_debug("Using CPUID %s\n", cpuid);
+
+ 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) {
+ pe = &map->table[i++];
+ if (!pe->name)
+ break;
+
+ /* 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);
+ }
+
+out:
+ free(cpuid);
+}
+
struct perf_event_attr * __weak
perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
{
@@ -497,6 +568,9 @@ static struct perf_pmu *pmu_lookup(const char *name)
if (pmu_aliases(name, &aliases))
return NULL;
+ if (!strcmp(name, "cpu"))
+ pmu_add_cpu_aliases(&aliases);
+
if (pmu_type(name, &type))
return NULL;
@@ -983,21 +1057,63 @@ 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;
+};
+
+static int cmp_sevent(const void *a, const void *b)
{
- const char * const *as = a;
- const char * const *bs = b;
- return strcmp(*as, *bs);
+ 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);
}
-void print_pmu_events(const char *event_glob, bool name_only)
+static void wordwrap(char *s, int start, int max, int corr)
+{
+ 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, 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;
@@ -1007,14 +1123,15 @@ 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 &&
@@ -1023,12 +1140,21 @@ void print_pmu_events(const char *event_glob, bool name_only)
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;
j++;
}
if (pmu->selectable &&
@@ -1036,25 +1162,39 @@ 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++) {
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");
+ } 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;