xref: /openbmc/linux/tools/perf/util/expr.c (revision fa831fbb)
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 "expr.h"
10 #include "expr-bison.h"
11 #include "expr-flex.h"
12 #include <linux/kernel.h>
13 #include <linux/zalloc.h>
14 #include <ctype.h>
15 
16 #ifdef PARSER_DEBUG
17 extern int expr_debug;
18 #endif
19 
20 struct expr_id_data {
21 	union {
22 		double val;
23 		struct {
24 			double val;
25 			const char *metric_name;
26 			const char *metric_expr;
27 		} ref;
28 		struct expr_id	*parent;
29 	};
30 
31 	enum {
32 		/* Holding a double value. */
33 		EXPR_ID_DATA__VALUE,
34 		/* Reference to another metric. */
35 		EXPR_ID_DATA__REF,
36 		/* A reference but the value has been computed. */
37 		EXPR_ID_DATA__REF_VALUE,
38 		/* A parent is remembered for the recursion check. */
39 		EXPR_ID_DATA__PARENT,
40 	} kind;
41 };
42 
43 static size_t key_hash(const void *key, void *ctx __maybe_unused)
44 {
45 	const char *str = (const char *)key;
46 	size_t hash = 0;
47 
48 	while (*str != '\0') {
49 		hash *= 31;
50 		hash += *str;
51 		str++;
52 	}
53 	return hash;
54 }
55 
56 static bool key_equal(const void *key1, const void *key2,
57 		    void *ctx __maybe_unused)
58 {
59 	return !strcmp((const char *)key1, (const char *)key2);
60 }
61 
62 struct hashmap *ids__new(void)
63 {
64 	return hashmap__new(key_hash, key_equal, NULL);
65 }
66 
67 void ids__free(struct hashmap *ids)
68 {
69 	struct hashmap_entry *cur;
70 	size_t bkt;
71 
72 	if (ids == NULL)
73 		return;
74 
75 	hashmap__for_each_entry(ids, cur, bkt) {
76 		free((char *)cur->key);
77 		free(cur->value);
78 	}
79 
80 	hashmap__free(ids);
81 }
82 
83 int ids__insert(struct hashmap *ids, const char *id,
84 		struct expr_id *parent)
85 {
86 	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
87 	char *old_key = NULL;
88 	int ret;
89 
90 	data_ptr = malloc(sizeof(*data_ptr));
91 	if (!data_ptr)
92 		return -ENOMEM;
93 
94 	data_ptr->parent = parent;
95 	data_ptr->kind = EXPR_ID_DATA__PARENT;
96 
97 	ret = hashmap__set(ids, id, data_ptr,
98 			   (const void **)&old_key, (void **)&old_data);
99 	if (ret)
100 		free(data_ptr);
101 	free(old_key);
102 	free(old_data);
103 	return ret;
104 }
105 
106 struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2)
107 {
108 	size_t bkt;
109 	struct hashmap_entry *cur;
110 	int ret;
111 	struct expr_id_data *old_data = NULL;
112 	char *old_key = NULL;
113 
114 	if (!ids1)
115 		return ids2;
116 
117 	if (!ids2)
118 		return ids1;
119 
120 	if (hashmap__size(ids1) <  hashmap__size(ids2)) {
121 		struct hashmap *tmp = ids1;
122 
123 		ids1 = ids2;
124 		ids2 = tmp;
125 	}
126 	hashmap__for_each_entry(ids2, cur, bkt) {
127 		ret = hashmap__set(ids1, cur->key, cur->value,
128 				(const void **)&old_key, (void **)&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, ctx->parent);
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 	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
152 	char *old_key = NULL;
153 	int ret;
154 
155 	data_ptr = malloc(sizeof(*data_ptr));
156 	if (!data_ptr)
157 		return -ENOMEM;
158 	data_ptr->val = val;
159 	data_ptr->kind = EXPR_ID_DATA__VALUE;
160 
161 	ret = hashmap__set(ctx->ids, id, data_ptr,
162 			   (const void **)&old_key, (void **)&old_data);
163 	if (ret)
164 		free(data_ptr);
165 	free(old_key);
166 	free(old_data);
167 	return ret;
168 }
169 
170 int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
171 {
172 	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
173 	char *old_key = NULL;
174 	char *name, *p;
175 	int ret;
176 
177 	data_ptr = zalloc(sizeof(*data_ptr));
178 	if (!data_ptr)
179 		return -ENOMEM;
180 
181 	name = strdup(ref->metric_name);
182 	if (!name) {
183 		free(data_ptr);
184 		return -ENOMEM;
185 	}
186 
187 	/*
188 	 * The jevents tool converts all metric expressions
189 	 * to lowercase, including metric references, hence
190 	 * we need to add lowercase name for metric, so it's
191 	 * properly found.
192 	 */
193 	for (p = name; *p; p++)
194 		*p = tolower(*p);
195 
196 	/*
197 	 * Intentionally passing just const char pointers,
198 	 * originally from 'struct pmu_event' object.
199 	 * We don't need to change them, so there's no
200 	 * need to create our own copy.
201 	 */
202 	data_ptr->ref.metric_name = ref->metric_name;
203 	data_ptr->ref.metric_expr = ref->metric_expr;
204 	data_ptr->kind = EXPR_ID_DATA__REF;
205 
206 	ret = hashmap__set(ctx->ids, name, data_ptr,
207 			   (const void **)&old_key, (void **)&old_data);
208 	if (ret)
209 		free(data_ptr);
210 
211 	pr_debug2("adding ref metric %s: %s\n",
212 		  ref->metric_name, ref->metric_expr);
213 
214 	free(old_key);
215 	free(old_data);
216 	return ret;
217 }
218 
219 int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
220 		 struct expr_id_data **data)
221 {
222 	return hashmap__find(ctx->ids, id, (void **)data) ? 0 : -1;
223 }
224 
225 int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
226 		     struct expr_id_data **datap)
227 {
228 	struct expr_id_data *data;
229 
230 	if (expr__get_id(ctx, id, datap) || !*datap) {
231 		pr_debug("%s not found\n", id);
232 		return -1;
233 	}
234 
235 	data = *datap;
236 
237 	switch (data->kind) {
238 	case EXPR_ID_DATA__VALUE:
239 		pr_debug2("lookup(%s): val %f\n", id, data->val);
240 		break;
241 	case EXPR_ID_DATA__PARENT:
242 		pr_debug2("lookup(%s): parent %s\n", id, data->parent->id);
243 		break;
244 	case EXPR_ID_DATA__REF:
245 		pr_debug2("lookup(%s): ref metric name %s\n", id,
246 			data->ref.metric_name);
247 		pr_debug("processing metric: %s ENTRY\n", id);
248 		data->kind = EXPR_ID_DATA__REF_VALUE;
249 		if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr)) {
250 			pr_debug("%s failed to count\n", id);
251 			return -1;
252 		}
253 		pr_debug("processing metric: %s EXIT: %f\n", id, data->val);
254 		break;
255 	case EXPR_ID_DATA__REF_VALUE:
256 		pr_debug2("lookup(%s): ref val %f metric name %s\n", id,
257 			data->ref.val, data->ref.metric_name);
258 		break;
259 	default:
260 		assert(0);  /* Unreachable. */
261 	}
262 
263 	return 0;
264 }
265 
266 void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
267 {
268 	struct expr_id_data *old_val = NULL;
269 	char *old_key = NULL;
270 
271 	hashmap__delete(ctx->ids, id,
272 			(const void **)&old_key, (void **)&old_val);
273 	free(old_key);
274 	free(old_val);
275 }
276 
277 struct expr_parse_ctx *expr__ctx_new(void)
278 {
279 	struct expr_parse_ctx *ctx;
280 
281 	ctx = malloc(sizeof(struct expr_parse_ctx));
282 	if (!ctx)
283 		return NULL;
284 
285 	ctx->ids = hashmap__new(key_hash, key_equal, NULL);
286 	ctx->parent = NULL;
287 	ctx->runtime = 0;
288 	return ctx;
289 }
290 
291 void expr__ctx_clear(struct expr_parse_ctx *ctx)
292 {
293 	struct hashmap_entry *cur;
294 	size_t bkt;
295 
296 	hashmap__for_each_entry(ctx->ids, cur, bkt) {
297 		free((char *)cur->key);
298 		free(cur->value);
299 	}
300 	hashmap__clear(ctx->ids);
301 }
302 
303 void expr__ctx_free(struct expr_parse_ctx *ctx)
304 {
305 	struct hashmap_entry *cur;
306 	size_t bkt;
307 
308 	hashmap__for_each_entry(ctx->ids, cur, bkt) {
309 		free((char *)cur->key);
310 		free(cur->value);
311 	}
312 	hashmap__free(ctx->ids);
313 	free(ctx);
314 }
315 
316 static int
317 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
318 	      bool compute_ids)
319 {
320 	struct expr_scanner_ctx scanner_ctx = {
321 		.runtime = ctx->runtime,
322 	};
323 	YY_BUFFER_STATE buffer;
324 	void *scanner;
325 	int ret;
326 
327 	pr_debug2("parsing metric: %s\n", expr);
328 
329 	ret = expr_lex_init_extra(&scanner_ctx, &scanner);
330 	if (ret)
331 		return ret;
332 
333 	buffer = expr__scan_string(expr, scanner);
334 
335 #ifdef PARSER_DEBUG
336 	expr_debug = 1;
337 	expr_set_debug(1, scanner);
338 #endif
339 
340 	ret = expr_parse(val, ctx, compute_ids, scanner);
341 
342 	expr__flush_buffer(buffer, scanner);
343 	expr__delete_buffer(buffer, scanner);
344 	expr_lex_destroy(scanner);
345 	return ret;
346 }
347 
348 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
349 		const char *expr)
350 {
351 	return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false) ? -1 : 0;
352 }
353 
354 int expr__find_ids(const char *expr, const char *one,
355 		   struct expr_parse_ctx *ctx)
356 {
357 	int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true);
358 
359 	if (one)
360 		expr__del_id(ctx, one);
361 
362 	return ret;
363 }
364 
365 double expr_id_data__value(const struct expr_id_data *data)
366 {
367 	if (data->kind == EXPR_ID_DATA__VALUE)
368 		return data->val;
369 	assert(data->kind == EXPR_ID_DATA__REF_VALUE);
370 	return data->ref.val;
371 }
372 
373 struct expr_id *expr_id_data__parent(struct expr_id_data *data)
374 {
375 	assert(data->kind == EXPR_ID_DATA__PARENT);
376 	return data->parent;
377 }
378