1 // SPDX-License-Identifier: GPL-2.0
7 #include "metricgroup.h"
13 #include <util/expr-bison.h>
14 #include <util/expr-flex.h>
15 #include "util/hashmap.h"
16 #include "util/header.h"
18 #include <perf/cpumap.h>
19 #include <linux/err.h>
20 #include <linux/kernel.h>
21 #include <linux/zalloc.h>
33 const char *metric_name
;
34 const char *metric_expr
;
39 /* Holding a double value. */
41 /* Reference to another metric. */
43 /* A reference but the value has been computed. */
44 EXPR_ID_DATA__REF_VALUE
,
48 static size_t key_hash(long key
, void *ctx __maybe_unused
)
50 const char *str
= (const char *)key
;
53 while (*str
!= '\0') {
61 static bool key_equal(long key1
, long key2
, void *ctx __maybe_unused
)
63 return !strcmp((const char *)key1
, (const char *)key2
);
66 struct hashmap
*ids__new(void)
70 hash
= hashmap__new(key_hash
, key_equal
, NULL
);
76 void ids__free(struct hashmap
*ids
)
78 struct hashmap_entry
*cur
;
84 hashmap__for_each_entry(ids
, cur
, bkt
) {
92 int ids__insert(struct hashmap
*ids
, const char *id
)
94 struct expr_id_data
*data_ptr
= NULL
, *old_data
= NULL
;
98 ret
= hashmap__set(ids
, id
, data_ptr
, &old_key
, &old_data
);
106 struct hashmap
*ids__union(struct hashmap
*ids1
, struct hashmap
*ids2
)
109 struct hashmap_entry
*cur
;
111 struct expr_id_data
*old_data
= NULL
;
112 char *old_key
= NULL
;
120 if (hashmap__size(ids1
) < hashmap__size(ids2
)) {
121 struct hashmap
*tmp
= ids1
;
126 hashmap__for_each_entry(ids2
, cur
, bkt
) {
127 ret
= hashmap__set(ids1
, cur
->key
, cur
->value
, &old_key
, &old_data
);
141 /* Caller must make sure id is allocated */
142 int expr__add_id(struct expr_parse_ctx
*ctx
, const char *id
)
144 return ids__insert(ctx
->ids
, id
);
147 /* Caller must make sure id is allocated */
148 int expr__add_id_val(struct expr_parse_ctx
*ctx
, const char *id
, double val
)
150 return expr__add_id_val_source_count(ctx
, id
, val
, /*source_count=*/1);
153 /* Caller must make sure id is allocated */
154 int expr__add_id_val_source_count(struct expr_parse_ctx
*ctx
, const char *id
,
155 double val
, int source_count
)
157 struct expr_id_data
*data_ptr
= NULL
, *old_data
= NULL
;
158 char *old_key
= NULL
;
161 data_ptr
= malloc(sizeof(*data_ptr
));
164 data_ptr
->val
.val
= val
;
165 data_ptr
->val
.source_count
= source_count
;
166 data_ptr
->kind
= EXPR_ID_DATA__VALUE
;
168 ret
= hashmap__set(ctx
->ids
, id
, data_ptr
, &old_key
, &old_data
);
176 int expr__add_ref(struct expr_parse_ctx
*ctx
, struct metric_ref
*ref
)
178 struct expr_id_data
*data_ptr
= NULL
, *old_data
= NULL
;
179 char *old_key
= NULL
;
183 data_ptr
= zalloc(sizeof(*data_ptr
));
187 name
= strdup(ref
->metric_name
);
194 * Intentionally passing just const char pointers,
195 * originally from 'struct pmu_event' object.
196 * We don't need to change them, so there's no
197 * need to create our own copy.
199 data_ptr
->ref
.metric_name
= ref
->metric_name
;
200 data_ptr
->ref
.metric_expr
= ref
->metric_expr
;
201 data_ptr
->kind
= EXPR_ID_DATA__REF
;
203 ret
= hashmap__set(ctx
->ids
, name
, data_ptr
, &old_key
, &old_data
);
207 pr_debug2("adding ref metric %s: %s\n",
208 ref
->metric_name
, ref
->metric_expr
);
215 int expr__get_id(struct expr_parse_ctx
*ctx
, const char *id
,
216 struct expr_id_data
**data
)
218 return hashmap__find(ctx
->ids
, id
, data
) ? 0 : -1;
221 bool expr__subset_of_ids(struct expr_parse_ctx
*haystack
,
222 struct expr_parse_ctx
*needles
)
224 struct hashmap_entry
*cur
;
226 struct expr_id_data
*data
;
228 hashmap__for_each_entry(needles
->ids
, cur
, bkt
) {
229 if (expr__get_id(haystack
, cur
->pkey
, &data
))
236 int expr__resolve_id(struct expr_parse_ctx
*ctx
, const char *id
,
237 struct expr_id_data
**datap
)
239 struct expr_id_data
*data
;
241 if (expr__get_id(ctx
, id
, datap
) || !*datap
) {
242 pr_debug("%s not found\n", id
);
248 switch (data
->kind
) {
249 case EXPR_ID_DATA__VALUE
:
250 pr_debug2("lookup(%s): val %f\n", id
, data
->val
.val
);
252 case EXPR_ID_DATA__REF
:
253 pr_debug2("lookup(%s): ref metric name %s\n", id
,
254 data
->ref
.metric_name
);
255 pr_debug("processing metric: %s ENTRY\n", id
);
256 data
->kind
= EXPR_ID_DATA__REF_VALUE
;
257 if (expr__parse(&data
->ref
.val
, ctx
, data
->ref
.metric_expr
)) {
258 pr_debug("%s failed to count\n", id
);
261 pr_debug("processing metric: %s EXIT: %f\n", id
, data
->ref
.val
);
263 case EXPR_ID_DATA__REF_VALUE
:
264 pr_debug2("lookup(%s): ref val %f metric name %s\n", id
,
265 data
->ref
.val
, data
->ref
.metric_name
);
268 assert(0); /* Unreachable. */
274 void expr__del_id(struct expr_parse_ctx
*ctx
, const char *id
)
276 struct expr_id_data
*old_val
= NULL
;
277 char *old_key
= NULL
;
279 hashmap__delete(ctx
->ids
, id
, &old_key
, &old_val
);
284 struct expr_parse_ctx
*expr__ctx_new(void)
286 struct expr_parse_ctx
*ctx
;
288 ctx
= malloc(sizeof(struct expr_parse_ctx
));
292 ctx
->ids
= hashmap__new(key_hash
, key_equal
, NULL
);
293 if (IS_ERR(ctx
->ids
)) {
297 ctx
->sctx
.user_requested_cpu_list
= NULL
;
298 ctx
->sctx
.runtime
= 0;
299 ctx
->sctx
.system_wide
= false;
304 void expr__ctx_clear(struct expr_parse_ctx
*ctx
)
306 struct hashmap_entry
*cur
;
309 hashmap__for_each_entry(ctx
->ids
, cur
, bkt
) {
313 hashmap__clear(ctx
->ids
);
316 void expr__ctx_free(struct expr_parse_ctx
*ctx
)
318 struct hashmap_entry
*cur
;
324 zfree(&ctx
->sctx
.user_requested_cpu_list
);
325 hashmap__for_each_entry(ctx
->ids
, cur
, bkt
) {
329 hashmap__free(ctx
->ids
);
334 __expr__parse(double *val
, struct expr_parse_ctx
*ctx
, const char *expr
,
337 YY_BUFFER_STATE buffer
;
341 pr_debug2("parsing metric: %s\n", expr
);
343 ret
= expr_lex_init_extra(&ctx
->sctx
, &scanner
);
347 buffer
= expr__scan_string(expr
, scanner
);
351 expr_set_debug(1, scanner
);
354 ret
= expr_parse(val
, ctx
, compute_ids
, scanner
);
356 expr__flush_buffer(buffer
, scanner
);
357 expr__delete_buffer(buffer
, scanner
);
358 expr_lex_destroy(scanner
);
362 int expr__parse(double *final_val
, struct expr_parse_ctx
*ctx
,
365 return __expr__parse(final_val
, ctx
, expr
, /*compute_ids=*/false) ? -1 : 0;
368 int expr__find_ids(const char *expr
, const char *one
,
369 struct expr_parse_ctx
*ctx
)
371 int ret
= __expr__parse(NULL
, ctx
, expr
, /*compute_ids=*/true);
374 expr__del_id(ctx
, one
);
379 double expr_id_data__value(const struct expr_id_data
*data
)
381 if (data
->kind
== EXPR_ID_DATA__VALUE
)
382 return data
->val
.val
;
383 assert(data
->kind
== EXPR_ID_DATA__REF_VALUE
);
384 return data
->ref
.val
;
387 double expr_id_data__source_count(const struct expr_id_data
*data
)
389 assert(data
->kind
== EXPR_ID_DATA__VALUE
);
390 return data
->val
.source_count
;
393 double expr__get_literal(const char *literal
, const struct expr_scanner_ctx
*ctx
)
396 enum tool_pmu_event ev
= tool_pmu__str_to_event(literal
+ 1);
398 if (ev
!= TOOL_PMU__EVENT_NONE
) {
401 if (tool_pmu__read_event(ev
, &count
))
404 pr_err("Failure to read '%s'", literal
);
406 } else if (!strcmp("#core_wide", literal
)) {
407 result
= core_wide(ctx
->system_wide
, ctx
->user_requested_cpu_list
)
410 pr_err("Unrecognized literal '%s'", literal
);
413 pr_debug2("literal: %s = %f\n", literal
, result
);
417 /* Does the event 'id' parse? Determine via ctx->ids if possible. */
418 double expr__has_event(const struct expr_parse_ctx
*ctx
, bool compute_ids
, const char *id
)
423 if (hashmap__find(ctx
->ids
, id
, /*value=*/NULL
))
433 if (strchr(id
, '@')) {
441 p
= strchr(tmp_id
, '@');
443 p
= strrchr(tmp_id
, '@');
445 ret
= parse_event(tmp
, tmp_id
) ? 0 : 1;
448 ret
= parse_event(tmp
, id
) ? 0 : 1;
455 double expr__strcmp_cpuid_str(const struct expr_parse_ctx
*ctx __maybe_unused
,
456 bool compute_ids __maybe_unused
, const char *test_id
)
459 struct perf_cpu cpu
= {-1};
460 char *cpuid
= get_cpuid_allow_env_override(cpu
);
465 ret
= !strcmp_cpuid_str(test_id
, cpuid
);