0001
0002 #include <stdbool.h>
0003 #include <assert.h>
0004 #include <errno.h>
0005 #include <stdlib.h>
0006 #include <string.h>
0007 #include "metricgroup.h"
0008 #include "cpumap.h"
0009 #include "cputopo.h"
0010 #include "debug.h"
0011 #include "expr.h"
0012 #include "expr-bison.h"
0013 #include "expr-flex.h"
0014 #include "smt.h"
0015 #include "tsc.h"
0016 #include <linux/err.h>
0017 #include <linux/kernel.h>
0018 #include <linux/zalloc.h>
0019 #include <ctype.h>
0020 #include <math.h>
0021
0022 #ifdef PARSER_DEBUG
0023 extern int expr_debug;
0024 #endif
0025
0026 struct expr_id_data {
0027 union {
0028 struct {
0029 double val;
0030 int source_count;
0031 } val;
0032 struct {
0033 double val;
0034 const char *metric_name;
0035 const char *metric_expr;
0036 } ref;
0037 };
0038
0039 enum {
0040
0041 EXPR_ID_DATA__VALUE,
0042
0043 EXPR_ID_DATA__REF,
0044
0045 EXPR_ID_DATA__REF_VALUE,
0046 } kind;
0047 };
0048
0049 static size_t key_hash(const void *key, void *ctx __maybe_unused)
0050 {
0051 const char *str = (const char *)key;
0052 size_t hash = 0;
0053
0054 while (*str != '\0') {
0055 hash *= 31;
0056 hash += *str;
0057 str++;
0058 }
0059 return hash;
0060 }
0061
0062 static bool key_equal(const void *key1, const void *key2,
0063 void *ctx __maybe_unused)
0064 {
0065 return !strcmp((const char *)key1, (const char *)key2);
0066 }
0067
0068 struct hashmap *ids__new(void)
0069 {
0070 struct hashmap *hash;
0071
0072 hash = hashmap__new(key_hash, key_equal, NULL);
0073 if (IS_ERR(hash))
0074 return NULL;
0075 return hash;
0076 }
0077
0078 void ids__free(struct hashmap *ids)
0079 {
0080 struct hashmap_entry *cur;
0081 size_t bkt;
0082
0083 if (ids == NULL)
0084 return;
0085
0086 hashmap__for_each_entry(ids, cur, bkt) {
0087 free((char *)cur->key);
0088 free(cur->value);
0089 }
0090
0091 hashmap__free(ids);
0092 }
0093
0094 int ids__insert(struct hashmap *ids, const char *id)
0095 {
0096 struct expr_id_data *data_ptr = NULL, *old_data = NULL;
0097 char *old_key = NULL;
0098 int ret;
0099
0100 ret = hashmap__set(ids, id, data_ptr,
0101 (const void **)&old_key, (void **)&old_data);
0102 if (ret)
0103 free(data_ptr);
0104 free(old_key);
0105 free(old_data);
0106 return ret;
0107 }
0108
0109 struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2)
0110 {
0111 size_t bkt;
0112 struct hashmap_entry *cur;
0113 int ret;
0114 struct expr_id_data *old_data = NULL;
0115 char *old_key = NULL;
0116
0117 if (!ids1)
0118 return ids2;
0119
0120 if (!ids2)
0121 return ids1;
0122
0123 if (hashmap__size(ids1) < hashmap__size(ids2)) {
0124 struct hashmap *tmp = ids1;
0125
0126 ids1 = ids2;
0127 ids2 = tmp;
0128 }
0129 hashmap__for_each_entry(ids2, cur, bkt) {
0130 ret = hashmap__set(ids1, cur->key, cur->value,
0131 (const void **)&old_key, (void **)&old_data);
0132 free(old_key);
0133 free(old_data);
0134
0135 if (ret) {
0136 hashmap__free(ids1);
0137 hashmap__free(ids2);
0138 return NULL;
0139 }
0140 }
0141 hashmap__free(ids2);
0142 return ids1;
0143 }
0144
0145
0146 int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
0147 {
0148 return ids__insert(ctx->ids, id);
0149 }
0150
0151
0152 int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val)
0153 {
0154 return expr__add_id_val_source_count(ctx, id, val, 1);
0155 }
0156
0157
0158 int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id,
0159 double val, int source_count)
0160 {
0161 struct expr_id_data *data_ptr = NULL, *old_data = NULL;
0162 char *old_key = NULL;
0163 int ret;
0164
0165 data_ptr = malloc(sizeof(*data_ptr));
0166 if (!data_ptr)
0167 return -ENOMEM;
0168 data_ptr->val.val = val;
0169 data_ptr->val.source_count = source_count;
0170 data_ptr->kind = EXPR_ID_DATA__VALUE;
0171
0172 ret = hashmap__set(ctx->ids, id, data_ptr,
0173 (const void **)&old_key, (void **)&old_data);
0174 if (ret)
0175 free(data_ptr);
0176 free(old_key);
0177 free(old_data);
0178 return ret;
0179 }
0180
0181 int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
0182 {
0183 struct expr_id_data *data_ptr = NULL, *old_data = NULL;
0184 char *old_key = NULL;
0185 char *name, *p;
0186 int ret;
0187
0188 data_ptr = zalloc(sizeof(*data_ptr));
0189 if (!data_ptr)
0190 return -ENOMEM;
0191
0192 name = strdup(ref->metric_name);
0193 if (!name) {
0194 free(data_ptr);
0195 return -ENOMEM;
0196 }
0197
0198
0199
0200
0201
0202
0203
0204 for (p = name; *p; p++)
0205 *p = tolower(*p);
0206
0207
0208
0209
0210
0211
0212
0213 data_ptr->ref.metric_name = ref->metric_name;
0214 data_ptr->ref.metric_expr = ref->metric_expr;
0215 data_ptr->kind = EXPR_ID_DATA__REF;
0216
0217 ret = hashmap__set(ctx->ids, name, data_ptr,
0218 (const void **)&old_key, (void **)&old_data);
0219 if (ret)
0220 free(data_ptr);
0221
0222 pr_debug2("adding ref metric %s: %s\n",
0223 ref->metric_name, ref->metric_expr);
0224
0225 free(old_key);
0226 free(old_data);
0227 return ret;
0228 }
0229
0230 int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
0231 struct expr_id_data **data)
0232 {
0233 return hashmap__find(ctx->ids, id, (void **)data) ? 0 : -1;
0234 }
0235
0236 bool expr__subset_of_ids(struct expr_parse_ctx *haystack,
0237 struct expr_parse_ctx *needles)
0238 {
0239 struct hashmap_entry *cur;
0240 size_t bkt;
0241 struct expr_id_data *data;
0242
0243 hashmap__for_each_entry(needles->ids, cur, bkt) {
0244 if (expr__get_id(haystack, cur->key, &data))
0245 return false;
0246 }
0247 return true;
0248 }
0249
0250
0251 int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
0252 struct expr_id_data **datap)
0253 {
0254 struct expr_id_data *data;
0255
0256 if (expr__get_id(ctx, id, datap) || !*datap) {
0257 pr_debug("%s not found\n", id);
0258 return -1;
0259 }
0260
0261 data = *datap;
0262
0263 switch (data->kind) {
0264 case EXPR_ID_DATA__VALUE:
0265 pr_debug2("lookup(%s): val %f\n", id, data->val.val);
0266 break;
0267 case EXPR_ID_DATA__REF:
0268 pr_debug2("lookup(%s): ref metric name %s\n", id,
0269 data->ref.metric_name);
0270 pr_debug("processing metric: %s ENTRY\n", id);
0271 data->kind = EXPR_ID_DATA__REF_VALUE;
0272 if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr)) {
0273 pr_debug("%s failed to count\n", id);
0274 return -1;
0275 }
0276 pr_debug("processing metric: %s EXIT: %f\n", id, data->ref.val);
0277 break;
0278 case EXPR_ID_DATA__REF_VALUE:
0279 pr_debug2("lookup(%s): ref val %f metric name %s\n", id,
0280 data->ref.val, data->ref.metric_name);
0281 break;
0282 default:
0283 assert(0);
0284 }
0285
0286 return 0;
0287 }
0288
0289 void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
0290 {
0291 struct expr_id_data *old_val = NULL;
0292 char *old_key = NULL;
0293
0294 hashmap__delete(ctx->ids, id,
0295 (const void **)&old_key, (void **)&old_val);
0296 free(old_key);
0297 free(old_val);
0298 }
0299
0300 struct expr_parse_ctx *expr__ctx_new(void)
0301 {
0302 struct expr_parse_ctx *ctx;
0303
0304 ctx = malloc(sizeof(struct expr_parse_ctx));
0305 if (!ctx)
0306 return NULL;
0307
0308 ctx->ids = hashmap__new(key_hash, key_equal, NULL);
0309 if (IS_ERR(ctx->ids)) {
0310 free(ctx);
0311 return NULL;
0312 }
0313 ctx->runtime = 0;
0314
0315 return ctx;
0316 }
0317
0318 void expr__ctx_clear(struct expr_parse_ctx *ctx)
0319 {
0320 struct hashmap_entry *cur;
0321 size_t bkt;
0322
0323 hashmap__for_each_entry(ctx->ids, cur, bkt) {
0324 free((char *)cur->key);
0325 free(cur->value);
0326 }
0327 hashmap__clear(ctx->ids);
0328 }
0329
0330 void expr__ctx_free(struct expr_parse_ctx *ctx)
0331 {
0332 struct hashmap_entry *cur;
0333 size_t bkt;
0334
0335 hashmap__for_each_entry(ctx->ids, cur, bkt) {
0336 free((char *)cur->key);
0337 free(cur->value);
0338 }
0339 hashmap__free(ctx->ids);
0340 free(ctx);
0341 }
0342
0343 static int
0344 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
0345 bool compute_ids)
0346 {
0347 struct expr_scanner_ctx scanner_ctx = {
0348 .runtime = ctx->runtime,
0349 };
0350 YY_BUFFER_STATE buffer;
0351 void *scanner;
0352 int ret;
0353
0354 pr_debug2("parsing metric: %s\n", expr);
0355
0356 ret = expr_lex_init_extra(&scanner_ctx, &scanner);
0357 if (ret)
0358 return ret;
0359
0360 buffer = expr__scan_string(expr, scanner);
0361
0362 #ifdef PARSER_DEBUG
0363 expr_debug = 1;
0364 expr_set_debug(1, scanner);
0365 #endif
0366
0367 ret = expr_parse(val, ctx, compute_ids, scanner);
0368
0369 expr__flush_buffer(buffer, scanner);
0370 expr__delete_buffer(buffer, scanner);
0371 expr_lex_destroy(scanner);
0372 return ret;
0373 }
0374
0375 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
0376 const char *expr)
0377 {
0378 return __expr__parse(final_val, ctx, expr, false) ? -1 : 0;
0379 }
0380
0381 int expr__find_ids(const char *expr, const char *one,
0382 struct expr_parse_ctx *ctx)
0383 {
0384 int ret = __expr__parse(NULL, ctx, expr, true);
0385
0386 if (one)
0387 expr__del_id(ctx, one);
0388
0389 return ret;
0390 }
0391
0392 double expr_id_data__value(const struct expr_id_data *data)
0393 {
0394 if (data->kind == EXPR_ID_DATA__VALUE)
0395 return data->val.val;
0396 assert(data->kind == EXPR_ID_DATA__REF_VALUE);
0397 return data->ref.val;
0398 }
0399
0400 double expr_id_data__source_count(const struct expr_id_data *data)
0401 {
0402 assert(data->kind == EXPR_ID_DATA__VALUE);
0403 return data->val.source_count;
0404 }
0405
0406 #if !defined(__i386__) && !defined(__x86_64__)
0407 double arch_get_tsc_freq(void)
0408 {
0409 return 0.0;
0410 }
0411 #endif
0412
0413 double expr__get_literal(const char *literal)
0414 {
0415 static struct cpu_topology *topology;
0416 double result = NAN;
0417
0418 if (!strcasecmp("#smt_on", literal)) {
0419 result = smt_on() > 0 ? 1.0 : 0.0;
0420 goto out;
0421 }
0422
0423 if (!strcmp("#num_cpus", literal)) {
0424 result = cpu__max_present_cpu().cpu;
0425 goto out;
0426 }
0427
0428 if (!strcasecmp("#system_tsc_freq", literal)) {
0429 result = arch_get_tsc_freq();
0430 goto out;
0431 }
0432
0433
0434
0435
0436
0437
0438
0439 if (!topology) {
0440 topology = cpu_topology__new();
0441 if (!topology) {
0442 pr_err("Error creating CPU topology");
0443 goto out;
0444 }
0445 }
0446 if (!strcmp("#num_packages", literal)) {
0447 result = topology->package_cpus_lists;
0448 goto out;
0449 }
0450 if (!strcmp("#num_dies", literal)) {
0451 result = topology->die_cpus_lists;
0452 goto out;
0453 }
0454 if (!strcmp("#num_cores", literal)) {
0455 result = topology->core_cpus_lists;
0456 goto out;
0457 }
0458
0459 pr_err("Unrecognized literal '%s'", literal);
0460 out:
0461 pr_debug2("literal: %s = %f\n", literal, result);
0462 return result;
0463 }