at v5.16-rc4 427 lines 9.2 kB view raw
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 21extern int expr_debug; 22#endif 23 24struct 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 47static 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 60static 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 66struct hashmap *ids__new(void) 67{ 68 return hashmap__new(key_hash, key_equal, NULL); 69} 70 71void 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 87int 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 102struct 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 */ 139int 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 */ 145int 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 */ 151int 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 174int 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 223int 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 229bool 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 244int 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 282void 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 293struct 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 307void 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 319void 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 332static 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 364int 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 370int 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 381double 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 389double 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 395double 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}