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 "evlist.h" 12 #include "expr.h" 13 #include "expr-bison.h" 14 #include "expr-flex.h" 15 #include "util/hashmap.h" 16 #include "smt.h" 17 #include "tsc.h" 18 #include <api/fs/fs.h> 19 #include <linux/err.h> 20 #include <linux/kernel.h> 21 #include <linux/zalloc.h> 22 #include <ctype.h> 23 #include <math.h> 24 #include "pmu.h" 25 26 #ifdef PARSER_DEBUG 27 extern int expr_debug; 28 #endif 29 30 struct expr_id_data { 31 union { 32 struct { 33 double val; 34 int source_count; 35 } val; 36 struct { 37 double val; 38 const char *metric_name; 39 const char *metric_expr; 40 } ref; 41 }; 42 43 enum { 44 /* Holding a double value. */ 45 EXPR_ID_DATA__VALUE, 46 /* Reference to another metric. */ 47 EXPR_ID_DATA__REF, 48 /* A reference but the value has been computed. */ 49 EXPR_ID_DATA__REF_VALUE, 50 } kind; 51 }; 52 53 static size_t key_hash(long key, void *ctx __maybe_unused) 54 { 55 const char *str = (const char *)key; 56 size_t hash = 0; 57 58 while (*str != '\0') { 59 hash *= 31; 60 hash += *str; 61 str++; 62 } 63 return hash; 64 } 65 66 static bool key_equal(long key1, long key2, void *ctx __maybe_unused) 67 { 68 return !strcmp((const char *)key1, (const char *)key2); 69 } 70 71 struct hashmap *ids__new(void) 72 { 73 struct hashmap *hash; 74 75 hash = hashmap__new(key_hash, key_equal, NULL); 76 if (IS_ERR(hash)) 77 return NULL; 78 return hash; 79 } 80 81 void ids__free(struct hashmap *ids) 82 { 83 struct hashmap_entry *cur; 84 size_t bkt; 85 86 if (ids == NULL) 87 return; 88 89 hashmap__for_each_entry(ids, cur, bkt) { 90 zfree(&cur->pkey); 91 zfree(&cur->pvalue); 92 } 93 94 hashmap__free(ids); 95 } 96 97 int ids__insert(struct hashmap *ids, const char *id) 98 { 99 struct expr_id_data *data_ptr = NULL, *old_data = NULL; 100 char *old_key = NULL; 101 int ret; 102 103 ret = hashmap__set(ids, id, data_ptr, &old_key, &old_data); 104 if (ret) 105 free(data_ptr); 106 free(old_key); 107 free(old_data); 108 return ret; 109 } 110 111 struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2) 112 { 113 size_t bkt; 114 struct hashmap_entry *cur; 115 int ret; 116 struct expr_id_data *old_data = NULL; 117 char *old_key = NULL; 118 119 if (!ids1) 120 return ids2; 121 122 if (!ids2) 123 return ids1; 124 125 if (hashmap__size(ids1) < hashmap__size(ids2)) { 126 struct hashmap *tmp = ids1; 127 128 ids1 = ids2; 129 ids2 = tmp; 130 } 131 hashmap__for_each_entry(ids2, cur, bkt) { 132 ret = hashmap__set(ids1, cur->key, cur->value, &old_key, &old_data); 133 free(old_key); 134 free(old_data); 135 136 if (ret) { 137 hashmap__free(ids1); 138 hashmap__free(ids2); 139 return NULL; 140 } 141 } 142 hashmap__free(ids2); 143 return ids1; 144 } 145 146 /* Caller must make sure id is allocated */ 147 int expr__add_id(struct expr_parse_ctx *ctx, const char *id) 148 { 149 return ids__insert(ctx->ids, id); 150 } 151 152 /* Caller must make sure id is allocated */ 153 int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val) 154 { 155 return expr__add_id_val_source_count(ctx, id, val, /*source_count=*/1); 156 } 157 158 /* Caller must make sure id is allocated */ 159 int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id, 160 double val, int source_count) 161 { 162 struct expr_id_data *data_ptr = NULL, *old_data = NULL; 163 char *old_key = NULL; 164 int ret; 165 166 data_ptr = malloc(sizeof(*data_ptr)); 167 if (!data_ptr) 168 return -ENOMEM; 169 data_ptr->val.val = val; 170 data_ptr->val.source_count = source_count; 171 data_ptr->kind = EXPR_ID_DATA__VALUE; 172 173 ret = hashmap__set(ctx->ids, id, data_ptr, &old_key, &old_data); 174 if (ret) 175 free(data_ptr); 176 free(old_key); 177 free(old_data); 178 return ret; 179 } 180 181 int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref) 182 { 183 struct expr_id_data *data_ptr = NULL, *old_data = NULL; 184 char *old_key = NULL; 185 char *name; 186 int ret; 187 188 data_ptr = zalloc(sizeof(*data_ptr)); 189 if (!data_ptr) 190 return -ENOMEM; 191 192 name = strdup(ref->metric_name); 193 if (!name) { 194 free(data_ptr); 195 return -ENOMEM; 196 } 197 198 /* 199 * Intentionally passing just const char pointers, 200 * originally from 'struct pmu_event' object. 201 * We don't need to change them, so there's no 202 * need to create our own copy. 203 */ 204 data_ptr->ref.metric_name = ref->metric_name; 205 data_ptr->ref.metric_expr = ref->metric_expr; 206 data_ptr->kind = EXPR_ID_DATA__REF; 207 208 ret = hashmap__set(ctx->ids, name, data_ptr, &old_key, &old_data); 209 if (ret) 210 free(data_ptr); 211 212 pr_debug2("adding ref metric %s: %s\n", 213 ref->metric_name, ref->metric_expr); 214 215 free(old_key); 216 free(old_data); 217 return ret; 218 } 219 220 int expr__get_id(struct expr_parse_ctx *ctx, const char *id, 221 struct expr_id_data **data) 222 { 223 return hashmap__find(ctx->ids, id, data) ? 0 : -1; 224 } 225 226 bool expr__subset_of_ids(struct expr_parse_ctx *haystack, 227 struct expr_parse_ctx *needles) 228 { 229 struct hashmap_entry *cur; 230 size_t bkt; 231 struct expr_id_data *data; 232 233 hashmap__for_each_entry(needles->ids, cur, bkt) { 234 if (expr__get_id(haystack, cur->pkey, &data)) 235 return false; 236 } 237 return true; 238 } 239 240 241 int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id, 242 struct expr_id_data **datap) 243 { 244 struct expr_id_data *data; 245 246 if (expr__get_id(ctx, id, datap) || !*datap) { 247 pr_debug("%s not found\n", id); 248 return -1; 249 } 250 251 data = *datap; 252 253 switch (data->kind) { 254 case EXPR_ID_DATA__VALUE: 255 pr_debug2("lookup(%s): val %f\n", id, data->val.val); 256 break; 257 case EXPR_ID_DATA__REF: 258 pr_debug2("lookup(%s): ref metric name %s\n", id, 259 data->ref.metric_name); 260 pr_debug("processing metric: %s ENTRY\n", id); 261 data->kind = EXPR_ID_DATA__REF_VALUE; 262 if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr)) { 263 pr_debug("%s failed to count\n", id); 264 return -1; 265 } 266 pr_debug("processing metric: %s EXIT: %f\n", id, data->ref.val); 267 break; 268 case EXPR_ID_DATA__REF_VALUE: 269 pr_debug2("lookup(%s): ref val %f metric name %s\n", id, 270 data->ref.val, data->ref.metric_name); 271 break; 272 default: 273 assert(0); /* Unreachable. */ 274 } 275 276 return 0; 277 } 278 279 void expr__del_id(struct expr_parse_ctx *ctx, const char *id) 280 { 281 struct expr_id_data *old_val = NULL; 282 char *old_key = NULL; 283 284 hashmap__delete(ctx->ids, id, &old_key, &old_val); 285 free(old_key); 286 free(old_val); 287 } 288 289 struct expr_parse_ctx *expr__ctx_new(void) 290 { 291 struct expr_parse_ctx *ctx; 292 293 ctx = malloc(sizeof(struct expr_parse_ctx)); 294 if (!ctx) 295 return NULL; 296 297 ctx->ids = hashmap__new(key_hash, key_equal, NULL); 298 if (IS_ERR(ctx->ids)) { 299 free(ctx); 300 return NULL; 301 } 302 ctx->sctx.user_requested_cpu_list = NULL; 303 ctx->sctx.runtime = 0; 304 ctx->sctx.system_wide = false; 305 306 return ctx; 307 } 308 309 void expr__ctx_clear(struct expr_parse_ctx *ctx) 310 { 311 struct hashmap_entry *cur; 312 size_t bkt; 313 314 hashmap__for_each_entry(ctx->ids, cur, bkt) { 315 zfree(&cur->pkey); 316 zfree(&cur->pvalue); 317 } 318 hashmap__clear(ctx->ids); 319 } 320 321 void expr__ctx_free(struct expr_parse_ctx *ctx) 322 { 323 struct hashmap_entry *cur; 324 size_t bkt; 325 326 if (!ctx) 327 return; 328 329 zfree(&ctx->sctx.user_requested_cpu_list); 330 hashmap__for_each_entry(ctx->ids, cur, bkt) { 331 zfree(&cur->pkey); 332 zfree(&cur->pvalue); 333 } 334 hashmap__free(ctx->ids); 335 free(ctx); 336 } 337 338 static int 339 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr, 340 bool compute_ids) 341 { 342 YY_BUFFER_STATE buffer; 343 void *scanner; 344 int ret; 345 346 pr_debug2("parsing metric: %s\n", expr); 347 348 ret = expr_lex_init_extra(&ctx->sctx, &scanner); 349 if (ret) 350 return ret; 351 352 buffer = expr__scan_string(expr, scanner); 353 354 #ifdef PARSER_DEBUG 355 expr_debug = 1; 356 expr_set_debug(1, scanner); 357 #endif 358 359 ret = expr_parse(val, ctx, compute_ids, scanner); 360 361 expr__flush_buffer(buffer, scanner); 362 expr__delete_buffer(buffer, scanner); 363 expr_lex_destroy(scanner); 364 return ret; 365 } 366 367 int expr__parse(double *final_val, struct expr_parse_ctx *ctx, 368 const char *expr) 369 { 370 return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false) ? -1 : 0; 371 } 372 373 int expr__find_ids(const char *expr, const char *one, 374 struct expr_parse_ctx *ctx) 375 { 376 int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true); 377 378 if (one) 379 expr__del_id(ctx, one); 380 381 return ret; 382 } 383 384 double expr_id_data__value(const struct expr_id_data *data) 385 { 386 if (data->kind == EXPR_ID_DATA__VALUE) 387 return data->val.val; 388 assert(data->kind == EXPR_ID_DATA__REF_VALUE); 389 return data->ref.val; 390 } 391 392 double expr_id_data__source_count(const struct expr_id_data *data) 393 { 394 assert(data->kind == EXPR_ID_DATA__VALUE); 395 return data->val.source_count; 396 } 397 398 #if !defined(__i386__) && !defined(__x86_64__) 399 double arch_get_tsc_freq(void) 400 { 401 return 0.0; 402 } 403 #endif 404 405 static double has_pmem(void) 406 { 407 static bool has_pmem, cached; 408 const char *sysfs = sysfs__mountpoint(); 409 char path[PATH_MAX]; 410 411 if (!cached) { 412 snprintf(path, sizeof(path), "%s/firmware/acpi/tables/NFIT", sysfs); 413 has_pmem = access(path, F_OK) == 0; 414 cached = true; 415 } 416 return has_pmem ? 1.0 : 0.0; 417 } 418 419 double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx) 420 { 421 const struct cpu_topology *topology; 422 double result = NAN; 423 424 if (!strcmp("#num_cpus", literal)) { 425 result = cpu__max_present_cpu().cpu; 426 goto out; 427 } 428 429 if (!strcasecmp("#system_tsc_freq", literal)) { 430 result = arch_get_tsc_freq(); 431 goto out; 432 } 433 434 /* 435 * Assume that topology strings are consistent, such as CPUs "0-1" 436 * wouldn't be listed as "0,1", and so after deduplication the number of 437 * these strings gives an indication of the number of packages, dies, 438 * etc. 439 */ 440 if (!strcasecmp("#smt_on", literal)) { 441 result = smt_on() ? 1.0 : 0.0; 442 goto out; 443 } 444 if (!strcmp("#core_wide", literal)) { 445 result = core_wide(ctx->system_wide, ctx->user_requested_cpu_list) 446 ? 1.0 : 0.0; 447 goto out; 448 } 449 if (!strcmp("#num_packages", literal)) { 450 topology = online_topology(); 451 result = topology->package_cpus_lists; 452 goto out; 453 } 454 if (!strcmp("#num_dies", literal)) { 455 topology = online_topology(); 456 result = topology->die_cpus_lists; 457 goto out; 458 } 459 if (!strcmp("#num_cores", literal)) { 460 topology = online_topology(); 461 result = topology->core_cpus_lists; 462 goto out; 463 } 464 if (!strcmp("#slots", literal)) { 465 result = perf_pmu__cpu_slots_per_cycle(); 466 goto out; 467 } 468 if (!strcmp("#has_pmem", literal)) { 469 result = has_pmem(); 470 goto out; 471 } 472 473 pr_err("Unrecognized literal '%s'", literal); 474 out: 475 pr_debug2("literal: %s = %f\n", literal, result); 476 return result; 477 } 478 479 /* Does the event 'id' parse? Determine via ctx->ids if possible. */ 480 double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id) 481 { 482 struct evlist *tmp; 483 double ret; 484 485 if (hashmap__find(ctx->ids, id, /*value=*/NULL)) 486 return 1.0; 487 488 if (!compute_ids) 489 return 0.0; 490 491 tmp = evlist__new(); 492 if (!tmp) 493 return NAN; 494 ret = parse_event(tmp, id) ? 0 : 1; 495 evlist__delete(tmp); 496 return ret; 497 } 498