Adding formula object to interface formula definitions like:

  set {
       events {
               CY = cycles
               IN = instructions
               BR = branches
       }

       cpi      = CY / IN
       bdensity = IN / BR

       print cpi
       print bdensity
  }

The 'set' defines set of counter that share same events.
Each 'set' defines:
  events   - event string that would go into stat -e option
  counters - any number of counters based on above events

Each event and counter is defined as an assignment (via =)
to the name(tag), which could be later used within counter
formulas.

Each counter (cpi/branch-rate above) defines formula that
produces the counter number.

Formula grammar:

  expr: '-' expr       |
        expr '+' expr  |
        expr '-' expr  |
        expr '*' expr  |
        expr '/' expr  |
        value          |
        name

  where name could be any event or counter name(tag), like:
  (considering the architectural events name patchset
   is included)

  set {
        events {
                IN = instructions
                R0 = 
cpu/OFFCORE_RESPONSE_0,offcore_rsp=OFFCORE_RESPONSE.(DMND_IFETCH|LLC_MISS_LOCAL)/
        }

        kinst     = IN / 1000
        llc_imiss = R0 / kinst

        print llc_imiss
  }

It's possible to enable counter to be displayed via 'print'
label on separate line. Only counters marked like that
will be displayed to the user.

Interface:
  perf_formula__init
  - initialize perf_formula handler

  perf_formula__load
  - load file into the handler

  perf_formula__free
  - cleanup

  perf_formula__set
  - get 'set' handler

  perf_formula__evlist
  - update perf_evlist with needed events

  perf_formula__print
  - display output ratios

Signed-off-by: Jiri Olsa <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: Corey Ashford <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Ulrich Drepper <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Stephane Eranian <[email protected]>
---
 tools/perf/Makefile       |  11 +
 tools/perf/util/evlist.c  |  13 ++
 tools/perf/util/evlist.h  |   4 +
 tools/perf/util/formula.c | 513 ++++++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/formula.h | 137 +++++++++++++
 tools/perf/util/formula.l | 119 +++++++++++
 tools/perf/util/formula.y | 249 ++++++++++++++++++++++
 7 files changed, 1046 insertions(+)
 create mode 100644 tools/perf/util/formula.c
 create mode 100644 tools/perf/util/formula.h
 create mode 100644 tools/perf/util/formula.l
 create mode 100644 tools/perf/util/formula.y

diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index b0f164b..bcbc524 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -326,8 +326,15 @@ $(OUTPUT)util/pmu-flex.c: util/pmu.l 
$(OUTPUT)util/pmu-bison.c
 $(OUTPUT)util/pmu-bison.c: util/pmu.y
        $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c -p 
perf_pmu_
 
+$(OUTPUT)util/formula-flex.c: util/formula.l util/formula.y 
$(OUTPUT)util/formula-bison.c
+       $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/formula-flex.h 
$(PARSER_DEBUG_FLEX) -t util/formula.l > $(OUTPUT)util/formula-flex.c
+
+$(OUTPUT)util/formula-bison.c: util/formula.y
+       $(QUIET_BISON)$(BISON) -v util/formula.y -d $(PARSER_DEBUG_BISON) -o 
$(OUTPUT)util/formula-bison.c
+
 $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c 
$(OUTPUT)util/parse-events-bison.c
 $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
+$(OUTPUT)util/formula.o: $(OUTPUT)util/formula-flex.c 
$(OUTPUT)util/formula-bison.c
 
 LIB_FILE=$(OUTPUT)libperf.a
 
@@ -420,6 +427,7 @@ LIB_H += util/intlist.h
 LIB_H += util/perf_regs.h
 LIB_H += util/unwind.h
 LIB_H += util/vdso.h
+LIB_H += util/formula.h
 LIB_H += ui/helpline.h
 LIB_H += ui/progress.h
 LIB_H += ui/util.h
@@ -492,6 +500,9 @@ LIB_OBJS += $(OUTPUT)util/rblist.o
 LIB_OBJS += $(OUTPUT)util/intlist.o
 LIB_OBJS += $(OUTPUT)util/vdso.o
 LIB_OBJS += $(OUTPUT)util/stat.o
+LIB_OBJS += $(OUTPUT)util/formula.o
+LIB_OBJS += $(OUTPUT)util/formula-flex.o
+LIB_OBJS += $(OUTPUT)util/formula-bison.o
 
 LIB_OBJS += $(OUTPUT)ui/setup.o
 LIB_OBJS += $(OUTPUT)ui/helpline.o
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index f7c7278..ec69b2b 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -863,3 +863,16 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, 
FILE *fp)
 
        return printed + fprintf(fp, "\n");;
 }
+
+struct perf_evsel*
+perf_evlist__find_evsel_name(struct perf_evlist *evlist, char *name)
+{
+       struct perf_evsel *evsel;
+
+       list_for_each_entry(evsel, &evlist->entries, node) {
+               if (strstr(perf_evsel__name(evsel), name))
+                       return evsel;
+       }
+
+       return NULL;
+}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index dbfb59f..abf76a4 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -169,4 +169,8 @@ static inline bool perf_evlist__has_formulas(struct 
perf_evlist *evlist)
 {
        return evlist->formulas != NULL;
 }
+
+struct perf_evsel*
+perf_evlist__find_evsel_name(struct perf_evlist *evlist, char *name);
+
 #endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/formula.c b/tools/perf/util/formula.c
new file mode 100644
index 0000000..2edc720
--- /dev/null
+++ b/tools/perf/util/formula.c
@@ -0,0 +1,513 @@
+
+#include <linux/compiler.h>
+#include <stdio.h>
+#include "stat.h"
+#include "parse-events.h"
+#include "formula.h"
+#include "formula-bison.h"
+#define YY_EXTRA_TYPE int
+#include "formula-flex.h"
+
+#define FORMULA_SET_ALL ((void *) -1)
+
+#ifdef PARSER_DEBUG
+extern int perf_formula_debug;
+#endif
+int perf_formula_parse(void *_data, void *scanner);
+
+void perf_formula__init(struct perf_formula *f)
+{
+       memset(f, 0x0, sizeof(*f));
+       INIT_LIST_HEAD(&f->head_files);
+}
+
+static int scanner_expr(const char *str, void *data)
+{
+       YY_BUFFER_STATE buffer;
+       void *scanner;
+       int ret;
+
+       ret = perf_formula_lex_init_extra(PF_START_EXPR, &scanner);
+       if (ret)
+               return ret;
+
+       buffer = perf_formula__scan_string(str, scanner);
+
+#ifdef PARSER_DEBUG
+       perf_formula_debug = 1;
+#endif
+       ret = perf_formula_parse(data, scanner);
+
+       perf_formula__flush_buffer(buffer, scanner);
+       perf_formula__delete_buffer(buffer, scanner);
+       perf_formula_lex_destroy(scanner);
+       return ret;
+}
+
+static int scanner_config(FILE *file, void *data)
+{
+       void *scanner;
+       int ret;
+
+       ret = perf_formula_lex_init_extra(PF_START_CONFIG, &scanner);
+       if (ret)
+               return ret;
+
+       perf_formula_set_in(file, scanner);
+
+#ifdef PARSER_DEBUG
+       perf_formula_debug = 1;
+#endif
+       ret = perf_formula_parse(data, scanner);
+
+       perf_formula_lex_destroy(scanner);
+       return ret;
+}
+
+static int config_parse(struct perf_formula_file *file)
+{
+       FILE *f;
+       int ret;
+
+       f = fopen(file->path, "r");
+       if (!f)
+               return -EINVAL;
+
+       ret = scanner_config(f, file);
+
+       fclose(f);
+       return ret;
+}
+
+static int counter_init(struct perf_formula_counter *counter)
+{
+       struct perf_formula_expr expr = {
+               .test_only = true,
+       };
+
+       return scanner_expr(counter->formula, &expr);
+}
+
+static int set_init(struct perf_formula_set *set)
+{
+       struct perf_formula_counter *counter;
+       int ret = 0;
+
+       list_for_each_entry(counter, &set->head_counters, list) {
+               ret = counter_init(counter);
+               if (ret)
+                       break;
+
+               counter->set = set;
+       }
+
+       return ret;
+}
+
+static int file_init(struct perf_formula_file *file)
+{
+       struct perf_formula_set *set;
+       int ret;
+
+       ret = config_parse(file);
+
+       list_for_each_entry(set, &file->head_sets, list) {
+               ret = set_init(set);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+static void file_free(struct perf_formula_file *file)
+{
+       struct perf_formula_set *set;
+
+       list_for_each_entry(set, &file->head_sets, list) {
+               struct perf_formula_counter *counter;
+
+               list_for_each_entry(counter, &set->head_counters, list)
+                       free(counter);
+
+               free(set);
+       }
+
+       free(file->path);
+       free(file);
+}
+
+int perf_formula__load(struct perf_formula *f, char *path)
+{
+       struct perf_formula_file *file;
+       int ret;
+
+       file = zalloc(sizeof(*file));
+       if (!file)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&file->list);
+       INIT_LIST_HEAD(&file->head_sets);
+       file->path = strdup(path);
+
+       ret = file_init(file);
+       if (ret)
+               file_free(file);
+       else
+               list_add_tail(&file->list, &f->head_files);
+
+       return ret;
+}
+
+int perf_formula__free(struct perf_formula *f)
+{
+       struct perf_formula_file *file;
+
+       list_for_each_entry(file, &f->head_files, list)
+               file_free(file);
+
+       return 0;
+}
+
+enum {
+       CB_NEXT,
+       CB_OK,
+       CB_FAIL,
+};
+
+typedef int (*set_cb)(struct perf_formula_set *set, void *data);
+
+static int for_each_set(struct perf_formula *formula,
+                       set_cb cb, void *data)
+{
+       struct perf_formula_file *file;
+
+       list_for_each_entry(file, &formula->head_files, list) {
+               struct perf_formula_set *set;
+
+               list_for_each_entry(set, &file->head_sets, list) {
+                       int ret = cb(set, data);
+
+                       if (ret == CB_NEXT)
+                               continue;
+                       else if (ret == CB_OK)
+                               return 0;
+                       else if (ret == CB_FAIL)
+                               return -1;
+               }
+       }
+
+       return 0;
+}
+
+struct find_set_data {
+       struct perf_formula_set *set;
+       char *name;
+};
+
+static int find_set_cb(struct perf_formula_set *set, void *data)
+{
+       struct find_set_data *d = data;
+
+       if (strcmp(set->name, d->name))
+               return CB_NEXT;
+
+       d->set = set;
+       return CB_OK;
+}
+
+struct perf_formula_set*
+perf_formula__set(struct perf_formula *f, char *name)
+{
+       struct find_set_data data = {
+               .name = name,
+       };
+
+       if (!strcmp(name, "all"))
+               return FORMULA_SET_ALL;
+
+       if (for_each_set(f, find_set_cb, &data))
+               return NULL;
+
+       return data.set;
+}
+
+static int eval_counter(struct perf_formula_counter *counter,
+                       struct perf_formula_expr *expr)
+{
+       int ret;
+
+       ret = scanner_expr(counter->formula, expr);
+       if (!ret) {
+               counter->result = expr->result;
+               pr_debug2("formula counter eval %s = %F\n",
+                        counter->name, counter->result);
+       }
+
+       return ret;
+}
+
+static int eval_set_cb(struct perf_formula_set *set, void *data)
+{
+       struct perf_formula_expr *expr = data;
+       struct perf_formula_counter *counter;
+
+       expr->set = set;
+
+       pr_debug2("formula eval %s\n", set->name);
+
+       list_for_each_entry(counter, &set->head_counters, list) {
+               if (eval_counter(counter, expr)) {
+                       pr_err("failed to eval counter %s\n",
+                               counter->name);
+                       return CB_FAIL;
+               }
+
+               if (expr->print && counter->print)
+                       fprintf(expr->file,
+                               "%'18.8F %-25s\n",
+                               counter->result, counter->name);
+       }
+
+       return CB_NEXT;
+}
+
+int perf_formula__eval(struct perf_formula *f,
+                      struct perf_formula_set *set,
+                      struct perf_formula_expr *expr)
+{
+       if (set != FORMULA_SET_ALL)
+               return eval_set_cb(set, expr);
+
+       return for_each_set(f, eval_set_cb, expr);
+}
+
+int perf_formula__print(FILE *file,
+                       struct perf_formula *f,
+                       struct perf_formula_set *set,
+                       struct perf_evlist *evlist,
+                       struct perf_formula_value **values)
+{
+       struct perf_formula_expr expr = {
+               .set = set,
+               .evlist = evlist,
+               .values = values,
+               .print = true,
+               .file = file,
+       };
+
+       return perf_formula__eval(f, set, &expr);
+}
+
+static int set_evlist(struct perf_formula_set *set,
+                     struct perf_evlist *evlist)
+{
+       struct perf_evlist *evlist_tmp = evlist;
+
+       if (set->loaded)
+               return 0;
+
+       if (parse_events(evlist_tmp, set->events))
+               return -1;
+
+       set->loaded = true;
+
+       return 0;
+}
+
+static int evlist_cb(struct perf_formula_set *set, void *data)
+{
+       struct perf_evlist *evlist = data;
+
+       if (set_evlist(set, evlist))
+               return CB_FAIL;
+
+       return CB_NEXT;
+}
+
+int perf_formula__evlist(struct perf_formula *f,
+                        struct perf_formula_set *set,
+                        struct perf_evlist *evlist)
+{
+       if (set != FORMULA_SET_ALL)
+               return set_evlist(set, evlist);
+
+       return for_each_set(f, evlist_cb, (void *) evlist);
+}
+
+static struct perf_formula_counter*
+set_counter(struct perf_formula_set *set, struct perf_formula_config *config)
+{
+       struct perf_formula_counter *counter;
+
+       counter = zalloc(sizeof(*counter));
+       if (!counter)
+               return NULL;
+
+       INIT_LIST_HEAD(&counter->list);
+       counter->name    = config->ass.name;
+       counter->formula = config->ass.value;
+       counter->set     = set;
+       counter->print   = false;
+
+       list_add_tail(&counter->list, &set->head_counters);
+       return counter;
+}
+
+static int
+set_events(struct perf_formula_set *set, struct perf_formula_config *config)
+{
+       struct perf_formula_config *event;
+#define MAX_EVENTS 4096
+       char buf[MAX_EVENTS];
+
+       /* grouped by default */
+       buf[0] = '{';
+       buf[1] = 0x0;
+
+       list_for_each_entry(event, &config->events, list) {
+               struct perf_formula_event *e;
+
+               e = zalloc(sizeof(*e));
+               if (!e)
+                       goto out;
+
+               INIT_LIST_HEAD(&e->list);
+               e->name   = event->ass.name;
+               e->config = event->ass.value;
+               list_add_tail(&e->list, &set->head_events);
+
+               /* TODO length check */
+               strcat(buf, event->ass.value);
+
+               if (!list_is_last(&event->list, &config->events))
+                       strcat(buf, ",");
+       }
+
+       strcat(buf, "}");
+       set->events = strdup(buf);
+       return 0;
+
+ out:
+       /* TODO free everything ;-) */
+       return -ENOMEM;
+}
+
+static int
+set_print(struct perf_formula_set *set, struct perf_formula_config *config)
+{
+       struct perf_formula_counter *counter;
+
+       list_for_each_entry(counter, &set->head_counters, list) {
+               if (!strcmp(counter->name, config->print)) {
+                       counter->print = true;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+struct perf_formula_set*
+perf_formula_set__new(char *name, struct list_head *head)
+{
+       struct perf_formula_set *set;
+       struct perf_formula_config *config;
+
+       set = zalloc(sizeof(*set));
+       if (!set)
+               return NULL;
+
+       INIT_LIST_HEAD(&set->list);
+       INIT_LIST_HEAD(&set->head_counters);
+       INIT_LIST_HEAD(&set->head_events);
+
+       list_for_each_entry(config, head, list) {
+               switch (config->type) {
+               case PERF_FORMULA_CONFIG_EVENTS:
+                       if (set_events(set, config))
+                               goto out;
+                       break;
+
+               case PERF_FORMULA_CONFIG_PRINT:
+                       if (set_print(set, config))
+                               goto out;
+                       break;
+
+               case PERF_FORMULA_CONFIG_ASS:
+                       if (!set_counter(set, config))
+                               goto out;
+                       break;
+
+               default:
+                       BUG_ON(1);
+               }
+       }
+
+       set->name = strdup(name);
+       return set;
+
+ out:
+       free(set);
+       return NULL;
+}
+
+static struct perf_evsel*
+resolve_evsel(struct perf_formula_expr *expr, char *name)
+{
+       struct perf_formula_set *set = expr->set;
+       struct perf_formula_event *e;
+
+       list_for_each_entry(e, &set->head_events, list) {
+               if (strcmp(e->name, name))
+                       continue;
+
+               return perf_evlist__find_evsel_name(expr->evlist, e->config);
+       }
+
+       return NULL;
+}
+
+static struct perf_formula_counter*
+resolve_counter(struct perf_formula_expr *expr, char *name)
+{
+       struct perf_formula_set *set = expr->set;
+       struct perf_formula_counter *counter;
+
+       list_for_each_entry(counter, &set->head_counters, list) {
+               if (!strcmp(counter->name, name))
+                       return counter;
+       }
+
+       return NULL;
+}
+
+double perf_formula_expr__resolve(struct perf_formula_expr *expr,
+                                 char *name)
+{
+       struct perf_evsel *evsel;
+       double result = 0;
+
+       if (expr->test_only)
+               return 1;
+
+       evsel = resolve_evsel(expr, name);
+       if (evsel) {
+               struct perf_counts *counts = evsel->counts;
+               result = counts->aggr.val;
+       } else {
+               struct perf_formula_counter *counter;
+
+               counter = resolve_counter(expr, name);
+               if (counter)
+                       result = counter->result;
+               else {
+                       pr_err("formula: failed to resolve '%s'\n",
+                              name);
+                       result = -1;
+               }
+       }
+
+       pr_debug2("formula resolve %s = %F\n", name, result);
+       return result;
+}
diff --git a/tools/perf/util/formula.h b/tools/perf/util/formula.h
new file mode 100644
index 0000000..90325ec
--- /dev/null
+++ b/tools/perf/util/formula.h
@@ -0,0 +1,137 @@
+#ifndef __PERF_FORMULA
+#define __PERF_FORMULA
+
+/*
+ * format:
+ * set {
+ *      events {
+ *              CY = cycles
+ *              IN = instructions
+ *              BR = branches
+ *      }
+ *
+ *      cpi      = CY / IN
+ *      bdensity = IN / BR
+ *
+ *      print cpi
+ *      print bdensity
+ * }
+ *
+ * TODO add following syntax:
+ *      print cpi = CY / IN
+ */
+
+#include <linux/list.h>
+#include "evlist.h"
+
+struct perf_formula {
+       struct list_head head_files;
+};
+
+struct perf_formula_file {
+       char *path;
+
+       struct list_head head_sets;
+       struct list_head list;
+};
+
+struct perf_formula_set {
+       char *name;
+       char *events;
+       bool  loaded;
+
+       struct list_head head_events;
+       struct list_head head_counters;
+       struct list_head list;
+};
+
+struct perf_formula_event {
+       char *name;
+       char *config;
+
+       struct perf_evsel *evsel;
+       struct list_head list;
+};
+
+struct perf_formula_counter {
+       char  *name;
+       char  *formula;
+       bool   print;
+       double result;
+
+       struct perf_formula_set *set;
+       struct list_head list;
+};
+
+struct perf_formula_value {
+       char   *name;
+       double *ptr;
+};
+
+struct perf_formula_expr {
+       bool   test_only;
+       bool   print;
+
+       double result;
+       FILE  *file;
+
+       struct perf_evlist         *evlist;
+       struct perf_formula_set    *set;
+       struct perf_formula_value **values;
+};
+
+struct perf_formula_ass {
+       char *name;
+       char *value;
+};
+
+struct perf_formula_config {
+       enum {
+               PERF_FORMULA_CONFIG_EVENTS,
+               PERF_FORMULA_CONFIG_PRINT,
+               PERF_FORMULA_CONFIG_ASS,
+       } type;
+
+       union {
+               struct perf_formula_counter *counter;
+               struct perf_formula_ass      ass;
+               struct list_head             events;
+               char                        *print;
+       };
+
+       struct list_head list;
+};
+
+
+void perf_formula__init(struct perf_formula *f);
+
+int perf_formula__load(struct perf_formula *f, char *path);
+int perf_formula__free(struct perf_formula *f);
+
+struct perf_formula_set*
+perf_formula__set(struct perf_formula *f, char *name);
+
+int perf_formula__evlist(struct perf_formula *f,
+                        struct perf_formula_set *set,
+                        struct perf_evlist *evlist);
+
+int perf_formula__print(FILE *file,
+                       struct perf_formula *f,
+                       struct perf_formula_set *set,
+                       struct perf_evlist *evlist,
+                       struct perf_formula_value **values);
+
+struct perf_formula_counter*
+perf_formula_counter__new(char *name, struct list_head *head);
+
+int perf_formula__eval(struct perf_formula *f,
+                      struct perf_formula_set *set,
+                      struct perf_formula_expr *expr);
+
+struct perf_formula_set*
+perf_formula_set__new(char *name, struct list_head *head);
+
+double perf_formula_expr__resolve(struct perf_formula_expr *expr,
+                                 char *name);
+
+#endif /* __PERF_FORMULA */
diff --git a/tools/perf/util/formula.l b/tools/perf/util/formula.l
new file mode 100644
index 0000000..3e83a0e
--- /dev/null
+++ b/tools/perf/util/formula.l
@@ -0,0 +1,119 @@
+
+%option reentrant
+%option bison-bridge
+%option prefix="perf_formula_"
+%option stack
+
+%{
+#include "formula-bison.h"
+#include "formula.h"
+
+char *perf_formula_get_text(yyscan_t yyscanner);
+YYSTYPE *perf_formula_get_lval(yyscan_t yyscanner);
+
+static int __value(YYSTYPE *yylval, char *str, int base, int token)
+{
+       double num;
+
+       errno = 0;
+       num = strtoull(str, NULL, base);
+       if (errno)
+               return PF_ERROR;
+
+       yylval->num = num;
+       return token;
+}
+
+static int value(yyscan_t scanner, int base)
+{
+       YYSTYPE *yylval = perf_formula_get_lval(scanner);
+       char *text = perf_formula_get_text(scanner);
+
+       return __value(yylval, text, base, PF_VALUE);
+}
+
+static int str(yyscan_t scanner, int token)
+{
+       YYSTYPE *yylval = perf_formula_get_lval(scanner);
+       char *text = perf_formula_get_text(scanner);
+
+       yylval->str = strdup(text);
+       return token;
+}
+
+%}
+
+num_dec                [0-9]+
+num_hex                0x[a-fA-F0-9]+
+name           [a-zA-Z_*?][a-zA-Z0-9_*?\.-]*
+
+%x config
+%x expr
+%x config_eoln_str
+
+%%
+
+%{
+       {
+               int start_token;
+
+               start_token = perf_formula_get_extra(yyscanner);
+
+               if (start_token == PF_START_CONFIG)
+                       BEGIN(config);
+               else if (start_token == PF_START_EXPR)
+                       BEGIN(expr);
+
+               if (start_token) {
+                       perf_formula_set_extra(NULL, yyscanner);
+                       return start_token;
+               }
+       }
+%}
+
+<config>{
+"{"            { return '{'; }
+"}"            { return '}'; }
+"="            { BEGIN(config_eoln_str); return '='; }
+
+events         { return PF_EVENTS; }
+print          { return PF_PRINT; }
+{name}         { return str(yyscanner, PF_NAME); }
+
+\n             { }
+.              { }
+}
+
+<config_eoln_str>{
+[^\n]+         {
+                       str(yyscanner, PF_EOLN_STR);
+                       BEGIN(config);
+                       return PF_EOLN_STR;
+                }
+
+<<EOF>>                {
+                       BEGIN(config);
+               }
+}
+
+<expr>{
+"*"            { return '*'; }
+"-"            { return '-'; }
+"+"            { return '+'; }
+"/"            { return '/'; }
+"("            { return '('; }
+")"            { return ')'; }
+
+{name}         { return str(yyscanner, PF_NAME); }
+{num_dec}      { return value(yyscanner, 10); }
+{num_hex}      { return value(yyscanner, 16); }
+
+.              { }
+}
+
+%%
+
+int perf_formula_wrap(void *scanner __maybe_unused)
+{
+       return 1;
+}
diff --git a/tools/perf/util/formula.y b/tools/perf/util/formula.y
new file mode 100644
index 0000000..955264c
--- /dev/null
+++ b/tools/perf/util/formula.y
@@ -0,0 +1,249 @@
+%pure-parser
+%name-prefix "perf_formula_"
+%parse-param {void *_data}
+%parse-param {void *scanner}
+%lex-param {void* scanner}
+
+%left '+' '-' '*' '/'
+
+%{
+
+#define YYDEBUG 1
+
+#include "util.h"
+#include "formula.h"
+#include "formula-bison.h"
+
+extern int formula_lex(YYSTYPE* lvalp, void* scanner);
+
+
+#define ABORT() YYABORT
+
+#define ABORT_ON(val) \
+do { \
+       if (val) \
+               YYABORT; \
+} while (0)
+
+#define HEAD() ({                                              \
+       struct list_head *__head = zalloc(sizeof(*__head));     \
+       ABORT_ON(!__head);                                      \
+       INIT_LIST_HEAD(__head);                                 \
+       __head;                                                 \
+})
+
+#define CONFIG() ({                                            \
+       struct perf_formula_config *__config;                   \
+       __config = zalloc(sizeof(*__config));                   \
+       ABORT_ON(!__config);                                    \
+       INIT_LIST_HEAD(&__config->list);                        \
+       __config;                                               \
+})
+
+%}
+
+%token PF_START_CONFIG PF_START_EXPR
+%token PF_NAME
+%token PF_VALUE
+%token PF_FORMULA
+%token PF_DESC
+%token PF_EVENTS
+%token PF_EOLN_STR
+%token PF_ERROR
+%token PF_PRINT
+
+%type <str> PF_NAME
+%type <num> PF_VALUE
+%type <str> PF_EOLN_STR
+%type <head> set_def
+%type <config> set_token
+%type <config> events_def
+%type <config> ass
+%type <num> expr
+
+%union
+{
+       char *str;
+       double num;
+       struct list_head *head;
+       struct config *config;
+       struct perf_formula_counter *counter;
+}
+
+%%
+
+start:
+PF_START_CONFIG start_config
+|
+PF_START_EXPR start_expr
+
+start_config: sets
+
+sets:
+sets set | set
+
+set:
+PF_NAME '{' set_def '}'
+{
+       struct perf_formula_file *file = _data;
+       struct perf_formula_set *set;
+
+       set = perf_formula_set__new($1, $3);
+       ABORT_ON(!set);
+
+       list_add_tail(&set->list, &file->head_sets);
+}
+
+set_def:
+set_def set_token
+{
+       struct list_head *head = $1;
+       struct perf_formula_config *config = $2;
+
+       list_add_tail(&config->list, head);
+       $$ = head;
+}
+|
+set_token
+{
+       struct list_head *head = HEAD();
+       struct perf_formula_config *config = $1;
+
+       list_add_tail(&config->list, head);
+       $$ = head;
+}
+
+set_token:
+PF_EVENTS '{' events_def '}'
+{
+       $$ = $3;
+}
+|
+PF_PRINT PF_NAME
+{
+       struct perf_formula_config *config = CONFIG();
+
+       config->type  = PERF_FORMULA_CONFIG_PRINT;
+       config->print = strdup($2);
+
+       $$ = config;
+}
+|
+ass
+
+/*
+ * TODO add 'print ass' processing in here, like:
+ * ...
+ * |
+ * PF_PRINT ass
+ * {
+ * ...
+ * }
+ */
+
+events_def:
+events_def ass
+{
+       struct perf_formula_config *config = $1;
+       struct perf_formula_config *ass    = $2;
+
+       list_add_tail(&ass->list, &config->events);
+
+       $$ = config;
+}
+|
+ass
+{
+       struct perf_formula_config *config = CONFIG();
+       struct perf_formula_config *ass    = $1;
+
+       config->type = PERF_FORMULA_CONFIG_EVENTS;
+       INIT_LIST_HEAD(&config->events);
+       list_add_tail(&ass->list, &config->events);
+
+       $$ = config;
+}
+
+ass:
+PF_NAME '=' PF_EOLN_STR
+{
+       struct perf_formula_config *config = CONFIG();
+
+       config->type = PERF_FORMULA_CONFIG_ASS;
+       config->ass.name  = strdup($1);
+       config->ass.value = strdup($3);
+
+       $$ = config;
+}
+
+start_expr:
+expr
+{
+       struct perf_formula_expr *expr = _data;
+
+       expr->result = $1;
+}
+
+expr:
+PF_VALUE
+{
+       $$ = $1;
+       pr_debug2("value %F\n", $$);
+}
+|
+PF_NAME
+{
+       $$ = perf_formula_expr__resolve(_data, $1);
+}
+|
+'-' expr
+{
+       $$ = - $2;
+       pr_debug2("%F = - %F\n", $$, $2);
+}
+|
+expr '+' expr
+{
+       $$ = $1 + $3;
+       pr_debug2("%F = %F + %F\n", $$, $1, $3);
+}
+|
+expr '-' expr
+{
+       $$ = $1 - $3;
+       pr_debug2("%F = %F - %F\n", $$, $1, $3);
+}
+|
+expr '*' expr
+{
+       $$ = $1 * $3;
+       pr_debug2("%F = %F * %F\n", $$, $1, $3);
+}
+|
+expr '/' expr
+{
+       if (!$3) {
+               pr_debug2("formula division by zero\n");
+               struct perf_formula_expr *expr = _data;
+               expr->result = 0.0;
+               YYACCEPT;
+       }
+
+
+       $$ = $1 / $3;
+       pr_debug2("%F = %F / %F\n", $$, $1, $3);
+}
+|
+'(' expr ')'
+{
+       $$ = $2;
+       pr_debug2("( %F )\n", $$);
+}
+
+%%
+
+void perf_formula_error(void *data __maybe_unused,
+                       void *scanner __maybe_unused,
+                       char const *msg __maybe_unused)
+{
+}
-- 
1.7.11.7

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to