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