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