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