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