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