These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / tools / perf / util / pmu.c
index 4841167..e4b173d 100644 (file)
@@ -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++)