Merge branches 'acpi-pm' and 'pm-genirq'
[cascardo/linux.git] / tools / perf / util / parse-events.c
index 1e15df1..c659a3c 100644 (file)
@@ -6,10 +6,11 @@
 #include "parse-options.h"
 #include "parse-events.h"
 #include "exec_cmd.h"
-#include "linux/string.h"
+#include "string.h"
 #include "symbol.h"
 #include "cache.h"
 #include "header.h"
+#include "debug.h"
 #include <api/fs/debugfs.h>
 #include "parse-events-bison.h"
 #define YY_EXTRA_TYPE int
@@ -29,6 +30,15 @@ extern int parse_events_debug;
 #endif
 int parse_events_parse(void *data, void *scanner);
 
+static struct perf_pmu_event_symbol *perf_pmu_events_list;
+/*
+ * The variable indicates the number of supported pmu event symbols.
+ * 0 means not initialized and ready to init
+ * -1 means failed to init, don't try anymore
+ * >0 is the number of supported pmu event symbols
+ */
+static int perf_pmu_events_list_num;
+
 static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
        [PERF_COUNT_HW_CPU_CYCLES] = {
                .symbol = "cpu-cycles",
@@ -633,18 +643,28 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
                         char *name, struct list_head *head_config)
 {
        struct perf_event_attr attr;
+       struct perf_pmu_info info;
        struct perf_pmu *pmu;
        struct perf_evsel *evsel;
-       const char *unit;
-       double scale;
 
        pmu = perf_pmu__find(name);
        if (!pmu)
                return -EINVAL;
 
-       memset(&attr, 0, sizeof(attr));
+       if (pmu->default_config) {
+               memcpy(&attr, pmu->default_config,
+                      sizeof(struct perf_event_attr));
+       } else {
+               memset(&attr, 0, sizeof(attr));
+       }
 
-       if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))
+       if (!head_config) {
+               attr.type = pmu->type;
+               evsel = __add_event(list, idx, &attr, NULL, pmu->cpus);
+               return evsel ? 0 : -ENOMEM;
+       }
+
+       if (perf_pmu__check_alias(pmu, head_config, &info))
                return -EINVAL;
 
        /*
@@ -659,8 +679,8 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
        evsel = __add_event(list, idx, &attr, pmu_event_name(head_config),
                            pmu->cpus);
        if (evsel) {
-               evsel->unit = unit;
-               evsel->scale = scale;
+               evsel->unit = info.unit;
+               evsel->scale = info.scale;
        }
 
        return evsel ? 0 : -ENOMEM;
@@ -852,30 +872,111 @@ int parse_events_name(struct list_head *list, char *name)
        return 0;
 }
 
-static int parse_events__scanner(const char *str, void *data, int start_token);
+static int
+comp_pmu(const void *p1, const void *p2)
+{
+       struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1;
+       struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2;
 
-static int parse_events_fixup(int ret, const char *str, void *data,
-                             int start_token)
+       return strcmp(pmu1->symbol, pmu2->symbol);
+}
+
+static void perf_pmu__parse_cleanup(void)
 {
-       char *o = strdup(str);
-       char *s = NULL;
-       char *t = o;
-       char *p;
+       if (perf_pmu_events_list_num > 0) {
+               struct perf_pmu_event_symbol *p;
+               int i;
+
+               for (i = 0; i < perf_pmu_events_list_num; i++) {
+                       p = perf_pmu_events_list + i;
+                       free(p->symbol);
+               }
+               free(perf_pmu_events_list);
+               perf_pmu_events_list = NULL;
+               perf_pmu_events_list_num = 0;
+       }
+}
+
+#define SET_SYMBOL(str, stype)         \
+do {                                   \
+       p->symbol = str;                \
+       if (!p->symbol)                 \
+               goto err;               \
+       p->type = stype;                \
+} while (0)
+
+/*
+ * Read the pmu events list from sysfs
+ * Save it into perf_pmu_events_list
+ */
+static void perf_pmu__parse_init(void)
+{
+
+       struct perf_pmu *pmu = NULL;
+       struct perf_pmu_alias *alias;
        int len = 0;
 
-       if (!o)
-               return ret;
-       while ((p = strsep(&t, ",")) != NULL) {
-               if (s)
-                       str_append(&s, &len, ",");
-               str_append(&s, &len, "cpu/");
-               str_append(&s, &len, p);
-               str_append(&s, &len, "/");
+       pmu = perf_pmu__find("cpu");
+       if ((pmu == NULL) || list_empty(&pmu->aliases)) {
+               perf_pmu_events_list_num = -1;
+               return;
        }
-       free(o);
-       if (!s)
-               return -ENOMEM;
-       return parse_events__scanner(s, data, start_token);
+       list_for_each_entry(alias, &pmu->aliases, list) {
+               if (strchr(alias->name, '-'))
+                       len++;
+               len++;
+       }
+       perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len);
+       if (!perf_pmu_events_list)
+               return;
+       perf_pmu_events_list_num = len;
+
+       len = 0;
+       list_for_each_entry(alias, &pmu->aliases, list) {
+               struct perf_pmu_event_symbol *p = perf_pmu_events_list + len;
+               char *tmp = strchr(alias->name, '-');
+
+               if (tmp != NULL) {
+                       SET_SYMBOL(strndup(alias->name, tmp - alias->name),
+                                       PMU_EVENT_SYMBOL_PREFIX);
+                       p++;
+                       SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX);
+                       len += 2;
+               } else {
+                       SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL);
+                       len++;
+               }
+       }
+       qsort(perf_pmu_events_list, len,
+               sizeof(struct perf_pmu_event_symbol), comp_pmu);
+
+       return;
+err:
+       perf_pmu__parse_cleanup();
+}
+
+enum perf_pmu_event_symbol_type
+perf_pmu__parse_check(const char *name)
+{
+       struct perf_pmu_event_symbol p, *r;
+
+       /* scan kernel pmu events from sysfs if needed */
+       if (perf_pmu_events_list_num == 0)
+               perf_pmu__parse_init();
+       /*
+        * name "cpu" could be prefix of cpu-cycles or cpu// events.
+        * cpu-cycles has been handled by hardcode.
+        * So it must be cpu// events, not kernel pmu event.
+        */
+       if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu"))
+               return PMU_EVENT_SYMBOL_ERR;
+
+       p.symbol = strdup(name);
+       r = bsearch(&p, perf_pmu_events_list,
+                       (size_t) perf_pmu_events_list_num,
+                       sizeof(struct perf_pmu_event_symbol), comp_pmu);
+       free(p.symbol);
+       return r ? r->type : PMU_EVENT_SYMBOL_ERR;
 }
 
 static int parse_events__scanner(const char *str, void *data, int start_token)
@@ -898,8 +999,6 @@ static int parse_events__scanner(const char *str, void *data, int start_token)
        parse_events__flush_buffer(buffer, scanner);
        parse_events__delete_buffer(buffer, scanner);
        parse_events_lex_destroy(scanner);
-       if (ret && !strchr(str, '/'))
-               ret = parse_events_fixup(ret, str, data, start_token);
        return ret;
 }
 
@@ -934,6 +1033,7 @@ int parse_events(struct perf_evlist *evlist, const char *str)
        int ret;
 
        ret = parse_events__scanner(str, &data, PE_START_EVENTS);
+       perf_pmu__parse_cleanup();
        if (!ret) {
                int entries = data.idx - evlist->nr_entries;
                perf_evlist__splice_list_tail(evlist, &data.list, entries);
@@ -973,7 +1073,7 @@ int parse_filter(const struct option *opt, const char *str,
 
        if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
                fprintf(stderr,
-                       "-F option should follow a -e tracepoint option\n");
+                       "--filter option should follow a -e tracepoint option\n");
                return -1;
        }
 
@@ -1006,9 +1106,11 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
        struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
        char evt_path[MAXPATHLEN];
        char dir_path[MAXPATHLEN];
+       char sbuf[STRERR_BUFSIZE];
 
        if (debugfs_valid_mountpoint(tracing_events_path)) {
-               printf("  [ Tracepoints not available: %s ]\n", strerror(errno));
+               printf("  [ Tracepoints not available: %s ]\n",
+                       strerror_r(errno, sbuf, sizeof(sbuf)));
                return;
        }