xref: /linux/tools/perf/util/expr.c (revision e565ceb48bbf82cc4db4a42a33e378fe30d8d010)
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 "debug.h"
9 #include "evlist.h"
10 #include "expr.h"
11 #include "smt.h"
12 #include "tool_pmu.h"
13 #include <util/expr-bison.h>
14 #include <util/expr-flex.h>
15 #include "util/hashmap.h"
16 #include "util/header.h"
17 #include "util/pmu.h"
18 #include <perf/cpumap.h>
19 #include <linux/err.h>
20 #include <linux/kernel.h>
21 #include <linux/zalloc.h>
22 #include <ctype.h>
23 #include <math.h>
24 
25 struct expr_id_data {
26 	union {
27 		struct {
28 			double val;
29 			int source_count;
30 			int aggr_nr;
31 		} val;
32 		struct {
33 			double val;
34 			const char *metric_name;
35 			const char *metric_expr;
36 		} ref;
37 	};
38 
39 	enum {
40 		/* Holding a double value. */
41 		EXPR_ID_DATA__VALUE,
42 		/* Reference to another metric. */
43 		EXPR_ID_DATA__REF,
44 		/* A reference but the value has been computed. */
45 		EXPR_ID_DATA__REF_VALUE,
46 	} kind;
47 };
48 
49 static size_t key_hash(long key, void *ctx __maybe_unused)
50 {
51 	const char *str = (const char *)key;
52 	size_t hash = 0;
53 
54 	while (*str != '\0') {
55 		hash *= 31;
56 		hash += *str;
57 		str++;
58 	}
59 	return hash;
60 }
61 
62 static bool key_equal(long key1, long key2, 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 		zfree(&cur->pkey);
87 		zfree(&cur->pvalue);
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, &old_key, &old_data);
100 	if (ret)
101 		free(data_ptr);
102 	free(old_key);
103 	free(old_data);
104 	return ret;
105 }
106 
107 struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2)
108 {
109 	size_t bkt;
110 	struct hashmap_entry *cur;
111 	int ret;
112 	struct expr_id_data *old_data = NULL;
113 	char *old_key = NULL;
114 
115 	if (!ids1)
116 		return ids2;
117 
118 	if (!ids2)
119 		return ids1;
120 
121 	if (hashmap__size(ids1) <  hashmap__size(ids2)) {
122 		struct hashmap *tmp = ids1;
123 
124 		ids1 = ids2;
125 		ids2 = tmp;
126 	}
127 	hashmap__for_each_entry(ids2, cur, bkt) {
128 		ret = hashmap__set(ids1, cur->key, cur->value, &old_key, &old_data);
129 		free(old_key);
130 		free(old_data);
131 
132 		if (ret) {
133 			hashmap__free(ids1);
134 			hashmap__free(ids2);
135 			return NULL;
136 		}
137 	}
138 	hashmap__free(ids2);
139 	return ids1;
140 }
141 
142 /* Caller must make sure id is allocated */
143 int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
144 {
145 	return ids__insert(ctx->ids, id);
146 }
147 
148 /* Caller must make sure id is allocated */
149 int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val)
150 {
151 	return expr__add_id_val_source_count(ctx, id, val, /*source_count=*/1);
152 }
153 
154 /* Caller must make sure id is allocated */
155 int expr__add_id_val_source_count_aggr_nr(struct expr_parse_ctx *ctx, const char *id,
156 					  double val, int source_count, int aggr_nr)
157 {
158 	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
159 	char *old_key = NULL;
160 	int ret;
161 
162 	data_ptr = malloc(sizeof(*data_ptr));
163 	if (!data_ptr)
164 		return -ENOMEM;
165 	data_ptr->val.val = val;
166 	data_ptr->val.source_count = source_count;
167 	data_ptr->val.aggr_nr = aggr_nr;
168 	data_ptr->kind = EXPR_ID_DATA__VALUE;
169 
170 	ret = hashmap__set(ctx->ids, id, data_ptr, &old_key, &old_data);
171 	if (ret) {
172 		free(data_ptr);
173 	} else if (old_data) {
174 		data_ptr->val.val += old_data->val.val;
175 		data_ptr->val.source_count += old_data->val.source_count;
176 		data_ptr->val.aggr_nr += old_data->val.aggr_nr;
177 	}
178 	free(old_key);
179 	free(old_data);
180 	return ret;
181 }
182 
183 /* Caller must make sure id is allocated */
184 int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id,
185 				  double val, int source_count)
186 {
187 	return expr__add_id_val_source_count_aggr_nr(ctx, id, val, source_count, 1);
188 }
189 
190 int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
191 {
192 	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
193 	char *old_key = NULL;
194 	char *name;
195 	int ret;
196 
197 	data_ptr = zalloc(sizeof(*data_ptr));
198 	if (!data_ptr)
199 		return -ENOMEM;
200 
201 	name = strdup(ref->metric_name);
202 	if (!name) {
203 		free(data_ptr);
204 		return -ENOMEM;
205 	}
206 
207 	/*
208 	 * Intentionally passing just const char pointers,
209 	 * originally from 'struct pmu_event' object.
210 	 * We don't need to change them, so there's no
211 	 * need to create our own copy.
212 	 */
213 	data_ptr->ref.metric_name = ref->metric_name;
214 	data_ptr->ref.metric_expr = ref->metric_expr;
215 	data_ptr->kind = EXPR_ID_DATA__REF;
216 
217 	ret = hashmap__set(ctx->ids, name, data_ptr, &old_key, &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 	if (!ctx || !id)
233 		return -1;
234 	return hashmap__find(ctx->ids, id, data) ? 0 : -1;
235 }
236 
237 bool expr__subset_of_ids(struct expr_parse_ctx *haystack,
238 			 struct expr_parse_ctx *needles)
239 {
240 	struct hashmap_entry *cur;
241 	size_t bkt;
242 	struct expr_id_data *data;
243 
244 	hashmap__for_each_entry(needles->ids, cur, bkt) {
245 		if (expr__get_id(haystack, cur->pkey, &data))
246 			return false;
247 	}
248 	return true;
249 }
250 
251 
252 int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
253 		     struct expr_id_data **datap)
254 {
255 	struct expr_id_data *data;
256 
257 	if (expr__get_id(ctx, id, datap) || !*datap) {
258 		pr_debug("%s not found\n", id);
259 		return -1;
260 	}
261 
262 	data = *datap;
263 
264 	switch (data->kind) {
265 	case EXPR_ID_DATA__VALUE:
266 		pr_debug2("lookup(%s): val %f\n", id, data->val.val);
267 		break;
268 	case EXPR_ID_DATA__REF:
269 		pr_debug2("lookup(%s): ref metric name %s\n", id,
270 			data->ref.metric_name);
271 		pr_debug("processing metric: %s ENTRY\n", id);
272 		data->kind = EXPR_ID_DATA__REF_VALUE;
273 		if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr)) {
274 			pr_debug("%s failed to count\n", id);
275 			return -1;
276 		}
277 		pr_debug("processing metric: %s EXIT: %f\n", id, data->ref.val);
278 		break;
279 	case EXPR_ID_DATA__REF_VALUE:
280 		pr_debug2("lookup(%s): ref val %f metric name %s\n", id,
281 			data->ref.val, data->ref.metric_name);
282 		break;
283 	default:
284 		assert(0);  /* Unreachable. */
285 	}
286 
287 	return 0;
288 }
289 
290 void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
291 {
292 	struct expr_id_data *old_val = NULL;
293 	char *old_key = NULL;
294 
295 	hashmap__delete(ctx->ids, id, &old_key, &old_val);
296 	free(old_key);
297 	free(old_val);
298 }
299 
300 struct expr_parse_ctx *expr__ctx_new(void)
301 {
302 	struct expr_parse_ctx *ctx;
303 
304 	ctx = calloc(1, sizeof(struct expr_parse_ctx));
305 	if (!ctx)
306 		return NULL;
307 
308 	ctx->ids = hashmap__new(key_hash, key_equal, NULL);
309 	if (IS_ERR(ctx->ids)) {
310 		free(ctx);
311 		return NULL;
312 	}
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 		zfree(&cur->pkey);
324 		zfree(&cur->pvalue);
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 	if (!ctx)
335 		return;
336 
337 	zfree(&ctx->sctx.user_requested_cpu_list);
338 	hashmap__for_each_entry(ctx->ids, cur, bkt) {
339 		zfree(&cur->pkey);
340 		zfree(&cur->pvalue);
341 	}
342 	hashmap__free(ctx->ids);
343 	free(ctx);
344 }
345 
346 static int
347 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
348 	      bool compute_ids)
349 {
350 	YY_BUFFER_STATE buffer;
351 	void *scanner;
352 	int ret;
353 
354 	pr_debug2("parsing metric: %s\n", expr);
355 
356 	ret = expr_lex_init_extra(&ctx->sctx, &scanner);
357 	if (ret)
358 		return ret;
359 
360 	buffer = expr__scan_string(expr, scanner);
361 
362 #ifdef PARSER_DEBUG
363 	expr_debug = 1;
364 	expr_set_debug(1, scanner);
365 #endif
366 
367 	ret = expr_parse(val, ctx, compute_ids, scanner);
368 
369 	expr__flush_buffer(buffer, scanner);
370 	expr__delete_buffer(buffer, scanner);
371 	expr_lex_destroy(scanner);
372 	return ret;
373 }
374 
375 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
376 		const char *expr)
377 {
378 	return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false) ? -1 : 0;
379 }
380 
381 int expr__find_ids(const char *expr, const char *one,
382 		   struct expr_parse_ctx *ctx)
383 {
384 	int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true);
385 
386 	if (one)
387 		expr__del_id(ctx, one);
388 
389 	/* A positive value means syntax error, convert to -EINVAL */
390 	return ret > 0 ? -EINVAL : ret;
391 }
392 
393 double expr_id_data__value(const struct expr_id_data *data)
394 {
395 	if (data->kind == EXPR_ID_DATA__VALUE)
396 		return data->val.val;
397 	assert(data->kind == EXPR_ID_DATA__REF_VALUE);
398 	return data->ref.val;
399 }
400 
401 double expr_id_data__source_count(const struct expr_id_data *data)
402 {
403 	if (data->kind == EXPR_ID_DATA__VALUE)
404 		return data->val.source_count;
405 	return 1.0;
406 }
407 
408 double expr_id_data__aggr_nr(const struct expr_id_data *data)
409 {
410 	if (data->kind == EXPR_ID_DATA__VALUE)
411 		return data->val.aggr_nr;
412 	return 1.0;
413 }
414 
415 double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx)
416 {
417 	double result = NAN;
418 	enum tool_pmu_event ev = tool_pmu__str_to_event(literal + 1);
419 
420 	if (ev != TOOL_PMU__EVENT_NONE) {
421 		u64 count;
422 
423 		if (tool_pmu__read_event(ev, /*evsel=*/NULL,
424 					 ctx->system_wide, ctx->user_requested_cpu_list,
425 					 &count))
426 			result = count;
427 		else
428 			pr_err("Failure to read '%s'\n", literal);
429 	} else {
430 		pr_err("Unrecognized literal '%s'\n", literal);
431 	}
432 
433 	pr_debug2("literal: %s = %f\n", literal, result);
434 	return result;
435 }
436 
437 /* Does the event 'id' parse? Determine via ctx->ids if possible. */
438 double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id)
439 {
440 	struct evlist *tmp;
441 	double ret;
442 
443 	if (hashmap__find(ctx->ids, id, /*value=*/NULL))
444 		return 1.0;
445 
446 	if (!compute_ids)
447 		return 0.0;
448 
449 	tmp = evlist__new();
450 	if (!tmp)
451 		return NAN;
452 
453 	if (strchr(id, '@')) {
454 		char *tmp_id, *p;
455 
456 		tmp_id = strdup(id);
457 		if (!tmp_id) {
458 			ret = NAN;
459 			goto out;
460 		}
461 		p = strchr(tmp_id, '@');
462 		*p = '/';
463 		p = strrchr(tmp_id, '@');
464 		*p = '/';
465 		ret = parse_event(tmp, tmp_id) ? 0 : 1;
466 		free(tmp_id);
467 	} else {
468 		ret = parse_event(tmp, id) ? 0 : 1;
469 	}
470 out:
471 	evlist__delete(tmp);
472 	return ret;
473 }
474 
475 double expr__strcmp_cpuid_str(const struct expr_parse_ctx *ctx __maybe_unused,
476 		       bool compute_ids __maybe_unused, const char *test_id)
477 {
478 	double ret;
479 	struct perf_cpu cpu = {-1};
480 	char *cpuid = get_cpuid_allow_env_override(cpu);
481 
482 	if (!cpuid)
483 		return NAN;
484 
485 	ret = !strcmp_cpuid_str(test_id, cpuid);
486 
487 	free(cpuid);
488 	return ret;
489 }
490