1 // SPDX-License-Identifier: GPL-2.0
7 #include "metricgroup.h"
12 #include "expr-bison.h"
13 #include "expr-flex.h"
15 #include <linux/kernel.h>
16 #include <linux/zalloc.h>
21 extern int expr_debug;
29 const char *metric_name;
30 const char *metric_expr;
35 /* Holding a double value. */
37 /* Reference to another metric. */
39 /* A reference but the value has been computed. */
40 EXPR_ID_DATA__REF_VALUE,
44 static size_t key_hash(const void *key, void *ctx __maybe_unused)
46 const char *str = (const char *)key;
49 while (*str != '\0') {
57 static bool key_equal(const void *key1, const void *key2,
58 void *ctx __maybe_unused)
60 return !strcmp((const char *)key1, (const char *)key2);
63 struct hashmap *ids__new(void)
65 return hashmap__new(key_hash, key_equal, NULL);
68 void ids__free(struct hashmap *ids)
70 struct hashmap_entry *cur;
76 hashmap__for_each_entry(ids, cur, bkt) {
77 free((char *)cur->key);
84 int ids__insert(struct hashmap *ids, const char *id)
86 struct expr_id_data *data_ptr = NULL, *old_data = NULL;
90 ret = hashmap__set(ids, id, data_ptr,
91 (const void **)&old_key, (void **)&old_data);
99 struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2)
102 struct hashmap_entry *cur;
104 struct expr_id_data *old_data = NULL;
105 char *old_key = NULL;
113 if (hashmap__size(ids1) < hashmap__size(ids2)) {
114 struct hashmap *tmp = ids1;
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);
135 /* Caller must make sure id is allocated */
136 int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
138 return ids__insert(ctx->ids, id);
141 /* Caller must make sure id is allocated */
142 int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val)
144 struct expr_id_data *data_ptr = NULL, *old_data = NULL;
145 char *old_key = NULL;
148 data_ptr = malloc(sizeof(*data_ptr));
152 data_ptr->kind = EXPR_ID_DATA__VALUE;
154 ret = hashmap__set(ctx->ids, id, data_ptr,
155 (const void **)&old_key, (void **)&old_data);
163 int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
165 struct expr_id_data *data_ptr = NULL, *old_data = NULL;
166 char *old_key = NULL;
170 data_ptr = zalloc(sizeof(*data_ptr));
174 name = strdup(ref->metric_name);
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
186 for (p = name; *p; p++)
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.
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;
199 ret = hashmap__set(ctx->ids, name, data_ptr,
200 (const void **)&old_key, (void **)&old_data);
204 pr_debug2("adding ref metric %s: %s\n",
205 ref->metric_name, ref->metric_expr);
212 int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
213 struct expr_id_data **data)
215 return hashmap__find(ctx->ids, id, (void **)data) ? 0 : -1;
218 bool expr__subset_of_ids(struct expr_parse_ctx *haystack,
219 struct expr_parse_ctx *needles)
221 struct hashmap_entry *cur;
223 struct expr_id_data *data;
225 hashmap__for_each_entry(needles->ids, cur, bkt) {
226 if (expr__get_id(haystack, cur->key, &data))
233 int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
234 struct expr_id_data **datap)
236 struct expr_id_data *data;
238 if (expr__get_id(ctx, id, datap) || !*datap) {
239 pr_debug("%s not found\n", id);
245 switch (data->kind) {
246 case EXPR_ID_DATA__VALUE:
247 pr_debug2("lookup(%s): val %f\n", id, data->val);
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);
258 pr_debug("processing metric: %s EXIT: %f\n", id, data->val);
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);
265 assert(0); /* Unreachable. */
271 void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
273 struct expr_id_data *old_val = NULL;
274 char *old_key = NULL;
276 hashmap__delete(ctx->ids, id,
277 (const void **)&old_key, (void **)&old_val);
282 struct expr_parse_ctx *expr__ctx_new(void)
284 struct expr_parse_ctx *ctx;
286 ctx = malloc(sizeof(struct expr_parse_ctx));
290 ctx->ids = hashmap__new(key_hash, key_equal, NULL);
296 void expr__ctx_clear(struct expr_parse_ctx *ctx)
298 struct hashmap_entry *cur;
301 hashmap__for_each_entry(ctx->ids, cur, bkt) {
302 free((char *)cur->key);
305 hashmap__clear(ctx->ids);
308 void expr__ctx_free(struct expr_parse_ctx *ctx)
310 struct hashmap_entry *cur;
313 hashmap__for_each_entry(ctx->ids, cur, bkt) {
314 free((char *)cur->key);
317 hashmap__free(ctx->ids);
322 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
325 struct expr_scanner_ctx scanner_ctx = {
326 .runtime = ctx->runtime,
328 YY_BUFFER_STATE buffer;
332 pr_debug2("parsing metric: %s\n", expr);
334 ret = expr_lex_init_extra(&scanner_ctx, &scanner);
338 buffer = expr__scan_string(expr, scanner);
342 expr_set_debug(1, scanner);
345 ret = expr_parse(val, ctx, compute_ids, scanner);
347 expr__flush_buffer(buffer, scanner);
348 expr__delete_buffer(buffer, scanner);
349 expr_lex_destroy(scanner);
353 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
356 return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false) ? -1 : 0;
359 int expr__find_ids(const char *expr, const char *one,
360 struct expr_parse_ctx *ctx)
362 int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true);
365 expr__del_id(ctx, one);
370 double expr_id_data__value(const struct expr_id_data *data)
372 if (data->kind == EXPR_ID_DATA__VALUE)
374 assert(data->kind == EXPR_ID_DATA__REF_VALUE);
375 return data->ref.val;
378 double expr__get_literal(const char *literal)
380 static struct cpu_topology *topology;
382 if (!strcmp("#smt_on", literal))
383 return smt_on() > 0 ? 1.0 : 0.0;
385 if (!strcmp("#num_cpus", literal))
386 return cpu__max_present_cpu();
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,
395 topology = cpu_topology__new();
397 pr_err("Error creating CPU topology");
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;
408 pr_err("Unrecognized literal '%s'", literal);