perf expr: Add metric literals for topology.
[linux-2.6-microblaze.git] / tools / perf / util / expr.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdbool.h>
3 #include <assert.h>
4 #include <errno.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include "metricgroup.h"
8 #include "cpumap.h"
9 #include "cputopo.h"
10 #include "debug.h"
11 #include "expr.h"
12 #include "expr-bison.h"
13 #include "expr-flex.h"
14 #include "smt.h"
15 #include <linux/kernel.h>
16 #include <linux/zalloc.h>
17 #include <ctype.h>
18 #include <math.h>
19
20 #ifdef PARSER_DEBUG
21 extern int expr_debug;
22 #endif
23
24 struct expr_id_data {
25         union {
26                 double val;
27                 struct {
28                         double val;
29                         const char *metric_name;
30                         const char *metric_expr;
31                 } ref;
32         };
33
34         enum {
35                 /* Holding a double value. */
36                 EXPR_ID_DATA__VALUE,
37                 /* Reference to another metric. */
38                 EXPR_ID_DATA__REF,
39                 /* A reference but the value has been computed. */
40                 EXPR_ID_DATA__REF_VALUE,
41         } kind;
42 };
43
44 static size_t key_hash(const void *key, void *ctx __maybe_unused)
45 {
46         const char *str = (const char *)key;
47         size_t hash = 0;
48
49         while (*str != '\0') {
50                 hash *= 31;
51                 hash += *str;
52                 str++;
53         }
54         return hash;
55 }
56
57 static bool key_equal(const void *key1, const void *key2,
58                     void *ctx __maybe_unused)
59 {
60         return !strcmp((const char *)key1, (const char *)key2);
61 }
62
63 struct hashmap *ids__new(void)
64 {
65         return hashmap__new(key_hash, key_equal, NULL);
66 }
67
68 void ids__free(struct hashmap *ids)
69 {
70         struct hashmap_entry *cur;
71         size_t bkt;
72
73         if (ids == NULL)
74                 return;
75
76         hashmap__for_each_entry(ids, cur, bkt) {
77                 free((char *)cur->key);
78                 free(cur->value);
79         }
80
81         hashmap__free(ids);
82 }
83
84 int ids__insert(struct hashmap *ids, const char *id)
85 {
86         struct expr_id_data *data_ptr = NULL, *old_data = NULL;
87         char *old_key = NULL;
88         int ret;
89
90         ret = hashmap__set(ids, id, data_ptr,
91                            (const void **)&old_key, (void **)&old_data);
92         if (ret)
93                 free(data_ptr);
94         free(old_key);
95         free(old_data);
96         return ret;
97 }
98
99 struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2)
100 {
101         size_t bkt;
102         struct hashmap_entry *cur;
103         int ret;
104         struct expr_id_data *old_data = NULL;
105         char *old_key = NULL;
106
107         if (!ids1)
108                 return ids2;
109
110         if (!ids2)
111                 return ids1;
112
113         if (hashmap__size(ids1) <  hashmap__size(ids2)) {
114                 struct hashmap *tmp = ids1;
115
116                 ids1 = ids2;
117                 ids2 = tmp;
118         }
119         hashmap__for_each_entry(ids2, cur, bkt) {
120                 ret = hashmap__set(ids1, cur->key, cur->value,
121                                 (const void **)&old_key, (void **)&old_data);
122                 free(old_key);
123                 free(old_data);
124
125                 if (ret) {
126                         hashmap__free(ids1);
127                         hashmap__free(ids2);
128                         return NULL;
129                 }
130         }
131         hashmap__free(ids2);
132         return ids1;
133 }
134
135 /* Caller must make sure id is allocated */
136 int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
137 {
138         return ids__insert(ctx->ids, id);
139 }
140
141 /* Caller must make sure id is allocated */
142 int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val)
143 {
144         struct expr_id_data *data_ptr = NULL, *old_data = NULL;
145         char *old_key = NULL;
146         int ret;
147
148         data_ptr = malloc(sizeof(*data_ptr));
149         if (!data_ptr)
150                 return -ENOMEM;
151         data_ptr->val = val;
152         data_ptr->kind = EXPR_ID_DATA__VALUE;
153
154         ret = hashmap__set(ctx->ids, id, data_ptr,
155                            (const void **)&old_key, (void **)&old_data);
156         if (ret)
157                 free(data_ptr);
158         free(old_key);
159         free(old_data);
160         return ret;
161 }
162
163 int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
164 {
165         struct expr_id_data *data_ptr = NULL, *old_data = NULL;
166         char *old_key = NULL;
167         char *name, *p;
168         int ret;
169
170         data_ptr = zalloc(sizeof(*data_ptr));
171         if (!data_ptr)
172                 return -ENOMEM;
173
174         name = strdup(ref->metric_name);
175         if (!name) {
176                 free(data_ptr);
177                 return -ENOMEM;
178         }
179
180         /*
181          * The jevents tool converts all metric expressions
182          * to lowercase, including metric references, hence
183          * we need to add lowercase name for metric, so it's
184          * properly found.
185          */
186         for (p = name; *p; p++)
187                 *p = tolower(*p);
188
189         /*
190          * Intentionally passing just const char pointers,
191          * originally from 'struct pmu_event' object.
192          * We don't need to change them, so there's no
193          * need to create our own copy.
194          */
195         data_ptr->ref.metric_name = ref->metric_name;
196         data_ptr->ref.metric_expr = ref->metric_expr;
197         data_ptr->kind = EXPR_ID_DATA__REF;
198
199         ret = hashmap__set(ctx->ids, name, data_ptr,
200                            (const void **)&old_key, (void **)&old_data);
201         if (ret)
202                 free(data_ptr);
203
204         pr_debug2("adding ref metric %s: %s\n",
205                   ref->metric_name, ref->metric_expr);
206
207         free(old_key);
208         free(old_data);
209         return ret;
210 }
211
212 int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
213                  struct expr_id_data **data)
214 {
215         return hashmap__find(ctx->ids, id, (void **)data) ? 0 : -1;
216 }
217
218 bool expr__subset_of_ids(struct expr_parse_ctx *haystack,
219                          struct expr_parse_ctx *needles)
220 {
221         struct hashmap_entry *cur;
222         size_t bkt;
223         struct expr_id_data *data;
224
225         hashmap__for_each_entry(needles->ids, cur, bkt) {
226                 if (expr__get_id(haystack, cur->key, &data))
227                         return false;
228         }
229         return true;
230 }
231
232
233 int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
234                      struct expr_id_data **datap)
235 {
236         struct expr_id_data *data;
237
238         if (expr__get_id(ctx, id, datap) || !*datap) {
239                 pr_debug("%s not found\n", id);
240                 return -1;
241         }
242
243         data = *datap;
244
245         switch (data->kind) {
246         case EXPR_ID_DATA__VALUE:
247                 pr_debug2("lookup(%s): val %f\n", id, data->val);
248                 break;
249         case EXPR_ID_DATA__REF:
250                 pr_debug2("lookup(%s): ref metric name %s\n", id,
251                         data->ref.metric_name);
252                 pr_debug("processing metric: %s ENTRY\n", id);
253                 data->kind = EXPR_ID_DATA__REF_VALUE;
254                 if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr)) {
255                         pr_debug("%s failed to count\n", id);
256                         return -1;
257                 }
258                 pr_debug("processing metric: %s EXIT: %f\n", id, data->val);
259                 break;
260         case EXPR_ID_DATA__REF_VALUE:
261                 pr_debug2("lookup(%s): ref val %f metric name %s\n", id,
262                         data->ref.val, data->ref.metric_name);
263                 break;
264         default:
265                 assert(0);  /* Unreachable. */
266         }
267
268         return 0;
269 }
270
271 void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
272 {
273         struct expr_id_data *old_val = NULL;
274         char *old_key = NULL;
275
276         hashmap__delete(ctx->ids, id,
277                         (const void **)&old_key, (void **)&old_val);
278         free(old_key);
279         free(old_val);
280 }
281
282 struct expr_parse_ctx *expr__ctx_new(void)
283 {
284         struct expr_parse_ctx *ctx;
285
286         ctx = malloc(sizeof(struct expr_parse_ctx));
287         if (!ctx)
288                 return NULL;
289
290         ctx->ids = hashmap__new(key_hash, key_equal, NULL);
291         ctx->runtime = 0;
292
293         return ctx;
294 }
295
296 void expr__ctx_clear(struct expr_parse_ctx *ctx)
297 {
298         struct hashmap_entry *cur;
299         size_t bkt;
300
301         hashmap__for_each_entry(ctx->ids, cur, bkt) {
302                 free((char *)cur->key);
303                 free(cur->value);
304         }
305         hashmap__clear(ctx->ids);
306 }
307
308 void expr__ctx_free(struct expr_parse_ctx *ctx)
309 {
310         struct hashmap_entry *cur;
311         size_t bkt;
312
313         hashmap__for_each_entry(ctx->ids, cur, bkt) {
314                 free((char *)cur->key);
315                 free(cur->value);
316         }
317         hashmap__free(ctx->ids);
318         free(ctx);
319 }
320
321 static int
322 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
323               bool compute_ids)
324 {
325         struct expr_scanner_ctx scanner_ctx = {
326                 .runtime = ctx->runtime,
327         };
328         YY_BUFFER_STATE buffer;
329         void *scanner;
330         int ret;
331
332         pr_debug2("parsing metric: %s\n", expr);
333
334         ret = expr_lex_init_extra(&scanner_ctx, &scanner);
335         if (ret)
336                 return ret;
337
338         buffer = expr__scan_string(expr, scanner);
339
340 #ifdef PARSER_DEBUG
341         expr_debug = 1;
342         expr_set_debug(1, scanner);
343 #endif
344
345         ret = expr_parse(val, ctx, compute_ids, scanner);
346
347         expr__flush_buffer(buffer, scanner);
348         expr__delete_buffer(buffer, scanner);
349         expr_lex_destroy(scanner);
350         return ret;
351 }
352
353 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
354                 const char *expr)
355 {
356         return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false) ? -1 : 0;
357 }
358
359 int expr__find_ids(const char *expr, const char *one,
360                    struct expr_parse_ctx *ctx)
361 {
362         int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true);
363
364         if (one)
365                 expr__del_id(ctx, one);
366
367         return ret;
368 }
369
370 double expr_id_data__value(const struct expr_id_data *data)
371 {
372         if (data->kind == EXPR_ID_DATA__VALUE)
373                 return data->val;
374         assert(data->kind == EXPR_ID_DATA__REF_VALUE);
375         return data->ref.val;
376 }
377
378 double expr__get_literal(const char *literal)
379 {
380         static struct cpu_topology *topology;
381
382         if (!strcmp("#smt_on", literal))
383                 return smt_on() > 0 ? 1.0 : 0.0;
384
385         if (!strcmp("#num_cpus", literal))
386                 return cpu__max_present_cpu();
387
388         /*
389          * Assume that topology strings are consistent, such as CPUs "0-1"
390          * wouldn't be listed as "0,1", and so after deduplication the number of
391          * these strings gives an indication of the number of packages, dies,
392          * etc.
393          */
394         if (!topology) {
395                 topology = cpu_topology__new();
396                 if (!topology) {
397                         pr_err("Error creating CPU topology");
398                         return NAN;
399                 }
400         }
401         if (!strcmp("#num_packages", literal))
402                 return topology->package_cpus_lists;
403         if (!strcmp("#num_dies", literal))
404                 return topology->die_cpus_lists;
405         if (!strcmp("#num_cores", literal))
406                 return topology->core_cpus_lists;
407
408         pr_err("Unrecognized literal '%s'", literal);
409         return NAN;
410 }