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