summaryrefslogtreecommitdiffstats
path: root/kernel/tools/perf/util/pmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/tools/perf/util/pmu.c')
-rw-r--r--kernel/tools/perf/util/pmu.c158
1 files changed, 130 insertions, 28 deletions
diff --git a/kernel/tools/perf/util/pmu.c b/kernel/tools/perf/util/pmu.c
index 48411674d..e4b173dec 100644
--- a/kernel/tools/perf/util/pmu.c
+++ b/kernel/tools/perf/util/pmu.c
@@ -1,4 +1,5 @@
#include <linux/list.h>
+#include <linux/compiler.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
@@ -112,7 +113,11 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *
if (sret < 0)
goto error;
- scale[sret] = '\0';
+ if (scale[sret - 1] == '\n')
+ scale[sret - 1] = '\0';
+ else
+ scale[sret] = '\0';
+
/*
* save current locale
*/
@@ -154,7 +159,10 @@ static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *n
close(fd);
- alias->unit[sret] = '\0';
+ if (alias->unit[sret - 1] == '\n')
+ alias->unit[sret - 1] = '\0';
+ else
+ alias->unit[sret] = '\0';
return 0;
error:
@@ -198,17 +206,12 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
return 0;
}
-static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
+static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
+ char *desc __maybe_unused, char *val)
{
struct perf_pmu_alias *alias;
- char buf[256];
int ret;
- ret = fread(buf, 1, sizeof(buf), file);
- if (ret == 0)
- return -EINVAL;
- buf[ret] = 0;
-
alias = malloc(sizeof(*alias));
if (!alias)
return -ENOMEM;
@@ -218,26 +221,43 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
alias->unit[0] = '\0';
alias->per_pkg = false;
- ret = parse_events_terms(&alias->terms, buf);
+ ret = parse_events_terms(&alias->terms, val);
if (ret) {
+ pr_err("Cannot parse alias %s: %d\n", val, ret);
free(alias);
return ret;
}
alias->name = strdup(name);
- /*
- * load unit name and scale if available
- */
- perf_pmu__parse_unit(alias, dir, name);
- perf_pmu__parse_scale(alias, dir, name);
- perf_pmu__parse_per_pkg(alias, dir, name);
- perf_pmu__parse_snapshot(alias, dir, name);
+ if (dir) {
+ /*
+ * load unit name and scale if available
+ */
+ perf_pmu__parse_unit(alias, dir, name);
+ perf_pmu__parse_scale(alias, dir, name);
+ perf_pmu__parse_per_pkg(alias, dir, name);
+ perf_pmu__parse_snapshot(alias, dir, name);
+ }
list_add_tail(&alias->list, list);
return 0;
}
+static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
+{
+ char buf[256];
+ int ret;
+
+ ret = fread(buf, 1, sizeof(buf), file);
+ if (ret == 0)
+ return -EINVAL;
+
+ buf[ret] = 0;
+
+ return __perf_pmu__new_alias(list, dir, name, NULL, buf);
+}
+
static inline bool pmu_alias_info_file(char *name)
{
size_t len;
@@ -429,7 +449,7 @@ static struct cpu_map *pmu_cpumask(const char *name)
return cpus;
}
-struct perf_event_attr *__attribute__((weak))
+struct perf_event_attr * __weak
perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
{
return NULL;
@@ -518,7 +538,7 @@ struct perf_pmu *perf_pmu__find(const char *name)
}
static struct perf_pmu_format *
-pmu_find_format(struct list_head *formats, char *name)
+pmu_find_format(struct list_head *formats, const char *name)
{
struct perf_pmu_format *format;
@@ -529,6 +549,21 @@ pmu_find_format(struct list_head *formats, char *name)
return NULL;
}
+__u64 perf_pmu__format_bits(struct list_head *formats, const char *name)
+{
+ struct perf_pmu_format *format = pmu_find_format(formats, name);
+ __u64 bits = 0;
+ int fbit;
+
+ if (!format)
+ return 0;
+
+ for_each_set_bit(fbit, format->bits, PERF_PMU_FORMAT_BITS)
+ bits |= 1ULL << fbit;
+
+ return bits;
+}
+
/*
* Sets value based on the format definition (format parameter)
* and unformated value (value parameter).
@@ -550,6 +585,18 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v,
}
}
+static __u64 pmu_format_max_value(const unsigned long *format)
+{
+ int w;
+
+ w = bitmap_weight(format, PERF_PMU_FORMAT_BITS);
+ if (!w)
+ return 0;
+ if (w < 64)
+ return (1ULL << w) - 1;
+ return -1;
+}
+
/*
* Term is a string term, and might be a param-term. Try to look up it's value
* in the remaining terms.
@@ -579,6 +626,28 @@ static int pmu_resolve_param_term(struct parse_events_term *term,
return -1;
}
+static char *pmu_formats_string(struct list_head *formats)
+{
+ struct perf_pmu_format *format;
+ char *str;
+ struct strbuf buf;
+ unsigned i = 0;
+
+ if (!formats)
+ return NULL;
+
+ strbuf_init(&buf, 0);
+ /* sysfs exported terms */
+ list_for_each_entry(format, formats, list)
+ strbuf_addf(&buf, i++ ? ",%s" : "%s",
+ format->name);
+
+ str = strbuf_detach(&buf, NULL);
+ strbuf_release(&buf);
+
+ return str;
+}
+
/*
* Setup one of config[12] attr members based on the
* user input data - term parameter.
@@ -587,11 +656,11 @@ static int pmu_config_term(struct list_head *formats,
struct perf_event_attr *attr,
struct parse_events_term *term,
struct list_head *head_terms,
- bool zero)
+ bool zero, struct parse_events_error *err)
{
struct perf_pmu_format *format;
__u64 *vp;
- __u64 val;
+ __u64 val, max_val;
/*
* If this is a parameter we've already used for parameterized-eval,
@@ -611,6 +680,14 @@ static int pmu_config_term(struct list_head *formats,
if (!format) {
if (verbose)
printf("Invalid event/parameter '%s'\n", term->config);
+ if (err) {
+ char *pmu_term = pmu_formats_string(formats);
+
+ err->idx = term->err_term;
+ err->str = strdup("unknown term");
+ err->help = parse_events_formats_error_string(pmu_term);
+ free(pmu_term);
+ }
return -EINVAL;
}
@@ -636,9 +713,14 @@ static int pmu_config_term(struct list_head *formats,
val = term->val.num;
else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
if (strcmp(term->val.str, "?")) {
- if (verbose)
+ if (verbose) {
pr_info("Invalid sysfs entry %s=%s\n",
term->config, term->val.str);
+ }
+ if (err) {
+ err->idx = term->err_val;
+ err->str = strdup("expected numeric value");
+ }
return -EINVAL;
}
@@ -647,6 +729,22 @@ static int pmu_config_term(struct list_head *formats,
} else
return -EINVAL;
+ max_val = pmu_format_max_value(format->bits);
+ if (val > max_val) {
+ if (err) {
+ err->idx = term->err_val;
+ if (asprintf(&err->str,
+ "value too big for format, maximum is %llu",
+ (unsigned long long)max_val) < 0)
+ err->str = strdup("value too big for format");
+ return -EINVAL;
+ }
+ /*
+ * Assume we don't care if !err, in which case the value will be
+ * silently truncated.
+ */
+ }
+
pmu_format_value(format->bits, val, vp, zero);
return 0;
}
@@ -654,12 +752,13 @@ static int pmu_config_term(struct list_head *formats,
int perf_pmu__config_terms(struct list_head *formats,
struct perf_event_attr *attr,
struct list_head *head_terms,
- bool zero)
+ bool zero, struct parse_events_error *err)
{
struct parse_events_term *term;
list_for_each_entry(term, head_terms, list) {
- if (pmu_config_term(formats, attr, term, head_terms, zero))
+ if (pmu_config_term(formats, attr, term, head_terms,
+ zero, err))
return -EINVAL;
}
@@ -672,12 +771,14 @@ int perf_pmu__config_terms(struct list_head *formats,
* 2) pmu format definitions - specified by pmu parameter
*/
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
- struct list_head *head_terms)
+ struct list_head *head_terms,
+ struct parse_events_error *err)
{
bool zero = !!pmu->default_config;
attr->type = pmu->type;
- return perf_pmu__config_terms(&pmu->format, attr, head_terms, zero);
+ return perf_pmu__config_terms(&pmu->format, attr, head_terms,
+ zero, err);
}
static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
@@ -907,7 +1008,8 @@ void print_pmu_events(const char *event_glob, bool name_only)
goto out_enomem;
j++;
}
- if (pmu->selectable) {
+ if (pmu->selectable &&
+ (event_glob == NULL || strglobmatch(pmu->name, event_glob))) {
char *s;
if (asprintf(&s, "%s//", pmu->name) < 0)
goto out_enomem;
@@ -925,7 +1027,7 @@ void print_pmu_events(const char *event_glob, bool name_only)
printf(" %-50s [Kernel PMU event]\n", aliases[j]);
printed++;
}
- if (printed)
+ if (printed && pager_in_use())
printf("\n");
out_free:
for (j = 0; j < len; j++)