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 23 #ifdef PARSER_DEBUG 24 extern int expr_debug; 25 #endif 26 27 struct expr_id_data { 28 union { 29 struct { 30 double val; 31 int source_count; 32 } val; 33 struct { 34 double val; 35 const char *metric_name; 36 const char *metric_expr; 37 } ref; 38 }; 39 40 enum { 41 /* Holding a double value. */ 42 EXPR_ID_DATA__VALUE, 43 /* Reference to another metric. */ 44 EXPR_ID_DATA__REF, 45 /* A reference but the value has been computed. */ 46 EXPR_ID_DATA__REF_VALUE, 47 } kind; 48 }; 49 50 static size_t key_hash(const void *key, void *ctx __maybe_unused) 51 { 52 const char *str = (const char *)key; 53 size_t hash = 0; 54 55 while (*str != '\0') { 56 hash *= 31; 57 hash += *str; 58 str++; 59 } 60 return hash; 61 } 62 63 static bool key_equal(const void *key1, const void *key2, 64 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((char *)cur->key); 89 free(cur->value); 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, 102 (const void **)&old_key, (void **)&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, 132 (const void **)&old_key, (void **)&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, 174 (const void **)&old_key, (void **)&old_data); 175 if (ret) 176 free(data_ptr); 177 free(old_key); 178 free(old_data); 179 return ret; 180 } 181 182 int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref) 183 { 184 struct expr_id_data *data_ptr = NULL, *old_data = NULL; 185 char *old_key = NULL; 186 char *name; 187 int ret; 188 189 data_ptr = zalloc(sizeof(*data_ptr)); 190 if (!data_ptr) 191 return -ENOMEM; 192 193 name = strdup(ref->metric_name); 194 if (!name) { 195 free(data_ptr); 196 return -ENOMEM; 197 } 198 199 /* 200 * Intentionally passing just const char pointers, 201 * originally from 'struct pmu_event' object. 202 * We don't need to change them, so there's no 203 * need to create our own copy. 204 */ 205 data_ptr->ref.metric_name = ref->metric_name; 206 data_ptr->ref.metric_expr = ref->metric_expr; 207 data_ptr->kind = EXPR_ID_DATA__REF; 208 209 ret = hashmap__set(ctx->ids, name, data_ptr, 210 (const void **)&old_key, (void **)&old_data); 211 if (ret) 212 free(data_ptr); 213 214 pr_debug2("adding ref metric %s: %s\n", 215 ref->metric_name, ref->metric_expr); 216 217 free(old_key); 218 free(old_data); 219 return ret; 220 } 221 222 int expr__get_id(struct expr_parse_ctx *ctx, const char *id, 223 struct expr_id_data **data) 224 { 225 return hashmap__find(ctx->ids, id, (void **)data) ? 0 : -1; 226 } 227 228 bool expr__subset_of_ids(struct expr_parse_ctx *haystack, 229 struct expr_parse_ctx *needles) 230 { 231 struct hashmap_entry *cur; 232 size_t bkt; 233 struct expr_id_data *data; 234 235 hashmap__for_each_entry(needles->ids, cur, bkt) { 236 if (expr__get_id(haystack, cur->key, &data)) 237 return false; 238 } 239 return true; 240 } 241 242 243 int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id, 244 struct expr_id_data **datap) 245 { 246 struct expr_id_data *data; 247 248 if (expr__get_id(ctx, id, datap) || !*datap) { 249 pr_debug("%s not found\n", id); 250 return -1; 251 } 252 253 data = *datap; 254 255 switch (data->kind) { 256 case EXPR_ID_DATA__VALUE: 257 pr_debug2("lookup(%s): val %f\n", id, data->val.val); 258 break; 259 case EXPR_ID_DATA__REF: 260 pr_debug2("lookup(%s): ref metric name %s\n", id, 261 data->ref.metric_name); 262 pr_debug("processing metric: %s ENTRY\n", id); 263 data->kind = EXPR_ID_DATA__REF_VALUE; 264 if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr)) { 265 pr_debug("%s failed to count\n", id); 266 return -1; 267 } 268 pr_debug("processing metric: %s EXIT: %f\n", id, data->ref.val); 269 break; 270 case EXPR_ID_DATA__REF_VALUE: 271 pr_debug2("lookup(%s): ref val %f metric name %s\n", id, 272 data->ref.val, data->ref.metric_name); 273 break; 274 default: 275 assert(0); /* Unreachable. */ 276 } 277 278 return 0; 279 } 280 281 void expr__del_id(struct expr_parse_ctx *ctx, const char *id) 282 { 283 struct expr_id_data *old_val = NULL; 284 char *old_key = NULL; 285 286 hashmap__delete(ctx->ids, id, 287 (const void **)&old_key, (void **)&old_val); 288 free(old_key); 289 free(old_val); 290 } 291 292 struct expr_parse_ctx *expr__ctx_new(void) 293 { 294 struct expr_parse_ctx *ctx; 295 296 ctx = malloc(sizeof(struct expr_parse_ctx)); 297 if (!ctx) 298 return NULL; 299 300 ctx->ids = hashmap__new(key_hash, key_equal, NULL); 301 if (IS_ERR(ctx->ids)) { 302 free(ctx); 303 return NULL; 304 } 305 ctx->sctx.user_requested_cpu_list = NULL; 306 ctx->sctx.runtime = 0; 307 ctx->sctx.system_wide = false; 308 309 return ctx; 310 } 311 312 void expr__ctx_clear(struct expr_parse_ctx *ctx) 313 { 314 struct hashmap_entry *cur; 315 size_t bkt; 316 317 hashmap__for_each_entry(ctx->ids, cur, bkt) { 318 free((char *)cur->key); 319 free(cur->value); 320 } 321 hashmap__clear(ctx->ids); 322 } 323 324 void expr__ctx_free(struct expr_parse_ctx *ctx) 325 { 326 struct hashmap_entry *cur; 327 size_t bkt; 328 329 if (!ctx) 330 return; 331 332 free(ctx->sctx.user_requested_cpu_list); 333 hashmap__for_each_entry(ctx->ids, cur, bkt) { 334 free((char *)cur->key); 335 free(cur->value); 336 } 337 hashmap__free(ctx->ids); 338 free(ctx); 339 } 340 341 static int 342 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr, 343 bool compute_ids) 344 { 345 YY_BUFFER_STATE buffer; 346 void *scanner; 347 int ret; 348 349 pr_debug2("parsing metric: %s\n", expr); 350 351 ret = expr_lex_init_extra(&ctx->sctx, &scanner); 352 if (ret) 353 return ret; 354 355 buffer = expr__scan_string(expr, scanner); 356 357 #ifdef PARSER_DEBUG 358 expr_debug = 1; 359 expr_set_debug(1, scanner); 360 #endif 361 362 ret = expr_parse(val, ctx, compute_ids, scanner); 363 364 expr__flush_buffer(buffer, scanner); 365 expr__delete_buffer(buffer, scanner); 366 expr_lex_destroy(scanner); 367 return ret; 368 } 369 370 int expr__parse(double *final_val, struct expr_parse_ctx *ctx, 371 const char *expr) 372 { 373 return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false) ? -1 : 0; 374 } 375 376 int expr__find_ids(const char *expr, const char *one, 377 struct expr_parse_ctx *ctx) 378 { 379 int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true); 380 381 if (one) 382 expr__del_id(ctx, one); 383 384 return ret; 385 } 386 387 double expr_id_data__value(const struct expr_id_data *data) 388 { 389 if (data->kind == EXPR_ID_DATA__VALUE) 390 return data->val.val; 391 assert(data->kind == EXPR_ID_DATA__REF_VALUE); 392 return data->ref.val; 393 } 394 395 double expr_id_data__source_count(const struct expr_id_data *data) 396 { 397 assert(data->kind == EXPR_ID_DATA__VALUE); 398 return data->val.source_count; 399 } 400 401 #if !defined(__i386__) && !defined(__x86_64__) 402 double arch_get_tsc_freq(void) 403 { 404 return 0.0; 405 } 406 #endif 407 408 double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx) 409 { 410 static struct cpu_topology *topology; 411 double result = NAN; 412 413 if (!strcmp("#num_cpus", literal)) { 414 result = cpu__max_present_cpu().cpu; 415 goto out; 416 } 417 418 if (!strcasecmp("#system_tsc_freq", literal)) { 419 result = arch_get_tsc_freq(); 420 goto out; 421 } 422 423 /* 424 * Assume that topology strings are consistent, such as CPUs "0-1" 425 * wouldn't be listed as "0,1", and so after deduplication the number of 426 * these strings gives an indication of the number of packages, dies, 427 * etc. 428 */ 429 if (!topology) { 430 topology = cpu_topology__new(); 431 if (!topology) { 432 pr_err("Error creating CPU topology"); 433 goto out; 434 } 435 } 436 if (!strcasecmp("#smt_on", literal)) { 437 result = smt_on(topology) ? 1.0 : 0.0; 438 goto out; 439 } 440 if (!strcmp("#core_wide", literal)) { 441 result = core_wide(ctx->system_wide, ctx->user_requested_cpu_list, topology) 442 ? 1.0 : 0.0; 443 goto out; 444 } 445 if (!strcmp("#num_packages", literal)) { 446 result = topology->package_cpus_lists; 447 goto out; 448 } 449 if (!strcmp("#num_dies", literal)) { 450 result = topology->die_cpus_lists; 451 goto out; 452 } 453 if (!strcmp("#num_cores", literal)) { 454 result = topology->core_cpus_lists; 455 goto out; 456 } 457 458 pr_err("Unrecognized literal '%s'", literal); 459 out: 460 pr_debug2("literal: %s = %f\n", literal, result); 461 return result; 462 } 463