Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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         /* Holding a double value. */
0041         EXPR_ID_DATA__VALUE,
0042         /* Reference to another metric. */
0043         EXPR_ID_DATA__REF,
0044         /* A reference but the value has been computed. */
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 /* Caller must make sure id is allocated */
0146 int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
0147 {
0148     return ids__insert(ctx->ids, id);
0149 }
0150 
0151 /* Caller must make sure id is allocated */
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, /*source_count=*/1);
0155 }
0156 
0157 /* Caller must make sure id is allocated */
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      * The jevents tool converts all metric expressions
0200      * to lowercase, including metric references, hence
0201      * we need to add lowercase name for metric, so it's
0202      * properly found.
0203      */
0204     for (p = name; *p; p++)
0205         *p = tolower(*p);
0206 
0207     /*
0208      * Intentionally passing just const char pointers,
0209      * originally from 'struct pmu_event' object.
0210      * We don't need to change them, so there's no
0211      * need to create our own copy.
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);  /* Unreachable. */
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, /*compute_ids=*/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, /*compute_ids=*/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      * Assume that topology strings are consistent, such as CPUs "0-1"
0435      * wouldn't be listed as "0,1", and so after deduplication the number of
0436      * these strings gives an indication of the number of packages, dies,
0437      * etc.
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 }