xref: /openbmc/linux/tools/perf/util/expr.c (revision cb94a02e)
1576a65b6SJiri Olsa // SPDX-License-Identifier: GPL-2.0
226226a97SJiri Olsa #include <stdbool.h>
3576a65b6SJiri Olsa #include <assert.h>
43744ca1eSArnaldo Carvalho de Melo #include <errno.h>
53744ca1eSArnaldo Carvalho de Melo #include <stdlib.h>
63744ca1eSArnaldo Carvalho de Melo #include <string.h>
7fc393839SJiri Olsa #include "metricgroup.h"
8fc393839SJiri Olsa #include "debug.h"
9576a65b6SJiri Olsa #include "expr.h"
1026226a97SJiri Olsa #include "expr-bison.h"
1126226a97SJiri Olsa #include "expr-flex.h"
12ded80bdaSIan Rogers #include <linux/kernel.h>
13fc393839SJiri Olsa #include <linux/zalloc.h>
14fc393839SJiri Olsa #include <ctype.h>
1526226a97SJiri Olsa 
1626226a97SJiri Olsa #ifdef PARSER_DEBUG
1726226a97SJiri Olsa extern int expr_debug;
1826226a97SJiri Olsa #endif
19576a65b6SJiri Olsa 
2029396cd5SIan Rogers struct expr_id_data {
2129396cd5SIan Rogers 	union {
2229396cd5SIan Rogers 		double val;
2329396cd5SIan Rogers 		struct {
2429396cd5SIan Rogers 			double val;
2529396cd5SIan Rogers 			const char *metric_name;
2629396cd5SIan Rogers 			const char *metric_expr;
2729396cd5SIan Rogers 		} ref;
2829396cd5SIan Rogers 		struct expr_id	*parent;
2929396cd5SIan Rogers 	};
3029396cd5SIan Rogers 
3129396cd5SIan Rogers 	enum {
3229396cd5SIan Rogers 		/* Holding a double value. */
3329396cd5SIan Rogers 		EXPR_ID_DATA__VALUE,
3429396cd5SIan Rogers 		/* Reference to another metric. */
3529396cd5SIan Rogers 		EXPR_ID_DATA__REF,
3629396cd5SIan Rogers 		/* A reference but the value has been computed. */
3729396cd5SIan Rogers 		EXPR_ID_DATA__REF_VALUE,
3829396cd5SIan Rogers 		/* A parent is remembered for the recursion check. */
3929396cd5SIan Rogers 		EXPR_ID_DATA__PARENT,
4029396cd5SIan Rogers 	} kind;
4129396cd5SIan Rogers };
4229396cd5SIan Rogers 
43ded80bdaSIan Rogers static size_t key_hash(const void *key, void *ctx __maybe_unused)
44576a65b6SJiri Olsa {
45ded80bdaSIan Rogers 	const char *str = (const char *)key;
46ded80bdaSIan Rogers 	size_t hash = 0;
47576a65b6SJiri Olsa 
48ded80bdaSIan Rogers 	while (*str != '\0') {
49ded80bdaSIan Rogers 		hash *= 31;
50ded80bdaSIan Rogers 		hash += *str;
51ded80bdaSIan Rogers 		str++;
52ded80bdaSIan Rogers 	}
53ded80bdaSIan Rogers 	return hash;
54ded80bdaSIan Rogers }
55ded80bdaSIan Rogers 
56ded80bdaSIan Rogers static bool key_equal(const void *key1, const void *key2,
57ded80bdaSIan Rogers 		    void *ctx __maybe_unused)
58ded80bdaSIan Rogers {
59ded80bdaSIan Rogers 	return !strcmp((const char *)key1, (const char *)key2);
60ded80bdaSIan Rogers }
61ded80bdaSIan Rogers 
62ded80bdaSIan Rogers /* Caller must make sure id is allocated */
63332603c2SJiri Olsa int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
64332603c2SJiri Olsa {
65332603c2SJiri Olsa 	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
66332603c2SJiri Olsa 	char *old_key = NULL;
67332603c2SJiri Olsa 	int ret;
68332603c2SJiri Olsa 
69332603c2SJiri Olsa 	data_ptr = malloc(sizeof(*data_ptr));
70332603c2SJiri Olsa 	if (!data_ptr)
71332603c2SJiri Olsa 		return -ENOMEM;
72332603c2SJiri Olsa 
73f6fb0960SJiri Olsa 	data_ptr->parent = ctx->parent;
7429396cd5SIan Rogers 	data_ptr->kind = EXPR_ID_DATA__PARENT;
75f6fb0960SJiri Olsa 
76*cb94a02eSIan Rogers 	ret = hashmap__set(ctx->ids, id, data_ptr,
77332603c2SJiri Olsa 			   (const void **)&old_key, (void **)&old_data);
78332603c2SJiri Olsa 	if (ret)
79332603c2SJiri Olsa 		free(data_ptr);
80332603c2SJiri Olsa 	free(old_key);
81332603c2SJiri Olsa 	free(old_data);
82332603c2SJiri Olsa 	return ret;
83332603c2SJiri Olsa }
84332603c2SJiri Olsa 
85332603c2SJiri Olsa /* Caller must make sure id is allocated */
86070b3b5aSJiri Olsa int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val)
87ded80bdaSIan Rogers {
88070b3b5aSJiri Olsa 	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
89ded80bdaSIan Rogers 	char *old_key = NULL;
90ded80bdaSIan Rogers 	int ret;
91ded80bdaSIan Rogers 
92070b3b5aSJiri Olsa 	data_ptr = malloc(sizeof(*data_ptr));
93070b3b5aSJiri Olsa 	if (!data_ptr)
94ded80bdaSIan Rogers 		return -ENOMEM;
95070b3b5aSJiri Olsa 	data_ptr->val = val;
9629396cd5SIan Rogers 	data_ptr->kind = EXPR_ID_DATA__VALUE;
97332603c2SJiri Olsa 
98*cb94a02eSIan Rogers 	ret = hashmap__set(ctx->ids, id, data_ptr,
99070b3b5aSJiri Olsa 			   (const void **)&old_key, (void **)&old_data);
10060e10c00SJiri Olsa 	if (ret)
10160e10c00SJiri Olsa 		free(data_ptr);
102ded80bdaSIan Rogers 	free(old_key);
103070b3b5aSJiri Olsa 	free(old_data);
104ded80bdaSIan Rogers 	return ret;
105ded80bdaSIan Rogers }
106ded80bdaSIan Rogers 
107fc393839SJiri Olsa int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
108fc393839SJiri Olsa {
109fc393839SJiri Olsa 	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
110fc393839SJiri Olsa 	char *old_key = NULL;
111fc393839SJiri Olsa 	char *name, *p;
112fc393839SJiri Olsa 	int ret;
113fc393839SJiri Olsa 
114fc393839SJiri Olsa 	data_ptr = zalloc(sizeof(*data_ptr));
115fc393839SJiri Olsa 	if (!data_ptr)
116fc393839SJiri Olsa 		return -ENOMEM;
117fc393839SJiri Olsa 
118fc393839SJiri Olsa 	name = strdup(ref->metric_name);
119fc393839SJiri Olsa 	if (!name) {
120fc393839SJiri Olsa 		free(data_ptr);
121fc393839SJiri Olsa 		return -ENOMEM;
122fc393839SJiri Olsa 	}
123fc393839SJiri Olsa 
124fc393839SJiri Olsa 	/*
125fc393839SJiri Olsa 	 * The jevents tool converts all metric expressions
126fc393839SJiri Olsa 	 * to lowercase, including metric references, hence
127fc393839SJiri Olsa 	 * we need to add lowercase name for metric, so it's
128fc393839SJiri Olsa 	 * properly found.
129fc393839SJiri Olsa 	 */
130fc393839SJiri Olsa 	for (p = name; *p; p++)
131fc393839SJiri Olsa 		*p = tolower(*p);
132fc393839SJiri Olsa 
133fc393839SJiri Olsa 	/*
134fc393839SJiri Olsa 	 * Intentionally passing just const char pointers,
135fc393839SJiri Olsa 	 * originally from 'struct pmu_event' object.
136fc393839SJiri Olsa 	 * We don't need to change them, so there's no
137fc393839SJiri Olsa 	 * need to create our own copy.
138fc393839SJiri Olsa 	 */
139fc393839SJiri Olsa 	data_ptr->ref.metric_name = ref->metric_name;
140fc393839SJiri Olsa 	data_ptr->ref.metric_expr = ref->metric_expr;
14129396cd5SIan Rogers 	data_ptr->kind = EXPR_ID_DATA__REF;
142fc393839SJiri Olsa 
143*cb94a02eSIan Rogers 	ret = hashmap__set(ctx->ids, name, data_ptr,
144fc393839SJiri Olsa 			   (const void **)&old_key, (void **)&old_data);
145fc393839SJiri Olsa 	if (ret)
146fc393839SJiri Olsa 		free(data_ptr);
147fc393839SJiri Olsa 
148fc393839SJiri Olsa 	pr_debug2("adding ref metric %s: %s\n",
149fc393839SJiri Olsa 		  ref->metric_name, ref->metric_expr);
150fc393839SJiri Olsa 
151fc393839SJiri Olsa 	free(old_key);
152fc393839SJiri Olsa 	free(old_data);
153fc393839SJiri Olsa 	return ret;
154fc393839SJiri Olsa }
155fc393839SJiri Olsa 
1565c5f5e83SJiri Olsa int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
1575c5f5e83SJiri Olsa 		 struct expr_id_data **data)
158ded80bdaSIan Rogers {
159*cb94a02eSIan Rogers 	return hashmap__find(ctx->ids, id, (void **)data) ? 0 : -1;
160576a65b6SJiri Olsa }
161576a65b6SJiri Olsa 
162acf71b05SJiri Olsa int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
163acf71b05SJiri Olsa 		     struct expr_id_data **datap)
164acf71b05SJiri Olsa {
165acf71b05SJiri Olsa 	struct expr_id_data *data;
166acf71b05SJiri Olsa 
167acf71b05SJiri Olsa 	if (expr__get_id(ctx, id, datap) || !*datap) {
168acf71b05SJiri Olsa 		pr_debug("%s not found\n", id);
169acf71b05SJiri Olsa 		return -1;
170acf71b05SJiri Olsa 	}
171acf71b05SJiri Olsa 
172acf71b05SJiri Olsa 	data = *datap;
173acf71b05SJiri Olsa 
17429396cd5SIan Rogers 	switch (data->kind) {
17529396cd5SIan Rogers 	case EXPR_ID_DATA__VALUE:
17629396cd5SIan Rogers 		pr_debug2("lookup(%s): val %f\n", id, data->val);
17729396cd5SIan Rogers 		break;
17829396cd5SIan Rogers 	case EXPR_ID_DATA__PARENT:
17929396cd5SIan Rogers 		pr_debug2("lookup(%s): parent %s\n", id, data->parent->id);
18029396cd5SIan Rogers 		break;
18129396cd5SIan Rogers 	case EXPR_ID_DATA__REF:
18229396cd5SIan Rogers 		pr_debug2("lookup(%s): ref metric name %s\n", id,
18329396cd5SIan Rogers 			data->ref.metric_name);
184acf71b05SJiri Olsa 		pr_debug("processing metric: %s ENTRY\n", id);
18529396cd5SIan Rogers 		data->kind = EXPR_ID_DATA__REF_VALUE;
18629396cd5SIan Rogers 		if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr, 1)) {
187acf71b05SJiri Olsa 			pr_debug("%s failed to count\n", id);
188acf71b05SJiri Olsa 			return -1;
189acf71b05SJiri Olsa 		}
190acf71b05SJiri Olsa 		pr_debug("processing metric: %s EXIT: %f\n", id, data->val);
19129396cd5SIan Rogers 		break;
19229396cd5SIan Rogers 	case EXPR_ID_DATA__REF_VALUE:
19329396cd5SIan Rogers 		pr_debug2("lookup(%s): ref val %f metric name %s\n", id,
19429396cd5SIan Rogers 			data->ref.val, data->ref.metric_name);
19529396cd5SIan Rogers 		break;
19629396cd5SIan Rogers 	default:
19729396cd5SIan Rogers 		assert(0);  /* Unreachable. */
198acf71b05SJiri Olsa 	}
199acf71b05SJiri Olsa 
200acf71b05SJiri Olsa 	return 0;
201acf71b05SJiri Olsa }
202acf71b05SJiri Olsa 
2033fd29fa6SJiri Olsa void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
2043fd29fa6SJiri Olsa {
2053fd29fa6SJiri Olsa 	struct expr_id_data *old_val = NULL;
2063fd29fa6SJiri Olsa 	char *old_key = NULL;
2073fd29fa6SJiri Olsa 
208*cb94a02eSIan Rogers 	hashmap__delete(ctx->ids, id,
2093fd29fa6SJiri Olsa 			(const void **)&old_key, (void **)&old_val);
2103fd29fa6SJiri Olsa 	free(old_key);
2113fd29fa6SJiri Olsa 	free(old_val);
2123fd29fa6SJiri Olsa }
2133fd29fa6SJiri Olsa 
214*cb94a02eSIan Rogers struct expr_parse_ctx *expr__ctx_new(void)
215576a65b6SJiri Olsa {
216*cb94a02eSIan Rogers 	struct expr_parse_ctx *ctx;
217*cb94a02eSIan Rogers 
218*cb94a02eSIan Rogers 	ctx = malloc(sizeof(struct expr_parse_ctx));
219*cb94a02eSIan Rogers 	if (!ctx)
220*cb94a02eSIan Rogers 		return NULL;
221*cb94a02eSIan Rogers 
222*cb94a02eSIan Rogers 	ctx->ids = hashmap__new(key_hash, key_equal, NULL);
223*cb94a02eSIan Rogers 	ctx->parent = NULL;
224*cb94a02eSIan Rogers 	return ctx;
225ded80bdaSIan Rogers }
226ded80bdaSIan Rogers 
227ded80bdaSIan Rogers void expr__ctx_clear(struct expr_parse_ctx *ctx)
228ded80bdaSIan Rogers {
229ded80bdaSIan Rogers 	struct hashmap_entry *cur;
230ded80bdaSIan Rogers 	size_t bkt;
231ded80bdaSIan Rogers 
232*cb94a02eSIan Rogers 	hashmap__for_each_entry(ctx->ids, cur, bkt) {
233ded80bdaSIan Rogers 		free((char *)cur->key);
234ded80bdaSIan Rogers 		free(cur->value);
235ded80bdaSIan Rogers 	}
236*cb94a02eSIan Rogers 	hashmap__clear(ctx->ids);
237*cb94a02eSIan Rogers }
238*cb94a02eSIan Rogers 
239*cb94a02eSIan Rogers void expr__ctx_free(struct expr_parse_ctx *ctx)
240*cb94a02eSIan Rogers {
241*cb94a02eSIan Rogers 	struct hashmap_entry *cur;
242*cb94a02eSIan Rogers 	size_t bkt;
243*cb94a02eSIan Rogers 
244*cb94a02eSIan Rogers 	hashmap__for_each_entry(ctx->ids, cur, bkt) {
245*cb94a02eSIan Rogers 		free((char *)cur->key);
246*cb94a02eSIan Rogers 		free(cur->value);
247*cb94a02eSIan Rogers 	}
248*cb94a02eSIan Rogers 	hashmap__free(ctx->ids);
249*cb94a02eSIan Rogers 	free(ctx);
250576a65b6SJiri Olsa }
25126226a97SJiri Olsa 
25226226a97SJiri Olsa static int
253aecce63eSJiri Olsa __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
2541e1a873dSKajol Jain 	      int start, int runtime)
25526226a97SJiri Olsa {
256871f9f59SJiri Olsa 	struct expr_scanner_ctx scanner_ctx = {
257871f9f59SJiri Olsa 		.start_token = start,
2581e1a873dSKajol Jain 		.runtime = runtime,
259871f9f59SJiri Olsa 	};
26026226a97SJiri Olsa 	YY_BUFFER_STATE buffer;
26126226a97SJiri Olsa 	void *scanner;
26226226a97SJiri Olsa 	int ret;
26326226a97SJiri Olsa 
264acf71b05SJiri Olsa 	pr_debug2("parsing metric: %s\n", expr);
265acf71b05SJiri Olsa 
266871f9f59SJiri Olsa 	ret = expr_lex_init_extra(&scanner_ctx, &scanner);
26726226a97SJiri Olsa 	if (ret)
26826226a97SJiri Olsa 		return ret;
26926226a97SJiri Olsa 
27026226a97SJiri Olsa 	buffer = expr__scan_string(expr, scanner);
27126226a97SJiri Olsa 
27226226a97SJiri Olsa #ifdef PARSER_DEBUG
27326226a97SJiri Olsa 	expr_debug = 1;
274e5e0e635SIan Rogers 	expr_set_debug(1, scanner);
27526226a97SJiri Olsa #endif
27626226a97SJiri Olsa 
27726226a97SJiri Olsa 	ret = expr_parse(val, ctx, scanner);
27826226a97SJiri Olsa 
27926226a97SJiri Olsa 	expr__flush_buffer(buffer, scanner);
28026226a97SJiri Olsa 	expr__delete_buffer(buffer, scanner);
28126226a97SJiri Olsa 	expr_lex_destroy(scanner);
28226226a97SJiri Olsa 	return ret;
28326226a97SJiri Olsa }
28426226a97SJiri Olsa 
285ded80bdaSIan Rogers int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
286ded80bdaSIan Rogers 		const char *expr, int runtime)
28726226a97SJiri Olsa {
2881e1a873dSKajol Jain 	return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 0;
28926226a97SJiri Olsa }
29026226a97SJiri Olsa 
291ded80bdaSIan Rogers int expr__find_other(const char *expr, const char *one,
292ded80bdaSIan Rogers 		     struct expr_parse_ctx *ctx, int runtime)
29326226a97SJiri Olsa {
294ded80bdaSIan Rogers 	int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
29526226a97SJiri Olsa 
2963fd29fa6SJiri Olsa 	if (one)
2973fd29fa6SJiri Olsa 		expr__del_id(ctx, one);
29826226a97SJiri Olsa 
299ded80bdaSIan Rogers 	return ret;
30026226a97SJiri Olsa }
30129396cd5SIan Rogers 
30229396cd5SIan Rogers double expr_id_data__value(const struct expr_id_data *data)
30329396cd5SIan Rogers {
30429396cd5SIan Rogers 	if (data->kind == EXPR_ID_DATA__VALUE)
30529396cd5SIan Rogers 		return data->val;
30629396cd5SIan Rogers 	assert(data->kind == EXPR_ID_DATA__REF_VALUE);
30729396cd5SIan Rogers 	return data->ref.val;
30829396cd5SIan Rogers }
30929396cd5SIan Rogers 
31029396cd5SIan Rogers struct expr_id *expr_id_data__parent(struct expr_id_data *data)
31129396cd5SIan Rogers {
31229396cd5SIan Rogers 	assert(data->kind == EXPR_ID_DATA__PARENT);
31329396cd5SIan Rogers 	return data->parent;
31429396cd5SIan Rogers }
315