linux/tools/perf/util/expr.c
<<
>>
Prefs
   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 "debug.h"
   9#include "expr.h"
  10#include "expr-bison.h"
  11#include "expr-flex.h"
  12#include <linux/kernel.h>
  13#include <linux/zalloc.h>
  14#include <ctype.h>
  15
  16#ifdef PARSER_DEBUG
  17extern int expr_debug;
  18#endif
  19
  20struct expr_id_data {
  21        union {
  22                double val;
  23                struct {
  24                        double val;
  25                        const char *metric_name;
  26                        const char *metric_expr;
  27                } ref;
  28                struct expr_id  *parent;
  29        };
  30
  31        enum {
  32                /* Holding a double value. */
  33                EXPR_ID_DATA__VALUE,
  34                /* Reference to another metric. */
  35                EXPR_ID_DATA__REF,
  36                /* A reference but the value has been computed. */
  37                EXPR_ID_DATA__REF_VALUE,
  38                /* A parent is remembered for the recursion check. */
  39                EXPR_ID_DATA__PARENT,
  40        } kind;
  41};
  42
  43static size_t key_hash(const void *key, void *ctx __maybe_unused)
  44{
  45        const char *str = (const char *)key;
  46        size_t hash = 0;
  47
  48        while (*str != '\0') {
  49                hash *= 31;
  50                hash += *str;
  51                str++;
  52        }
  53        return hash;
  54}
  55
  56static bool key_equal(const void *key1, const void *key2,
  57                    void *ctx __maybe_unused)
  58{
  59        return !strcmp((const char *)key1, (const char *)key2);
  60}
  61
  62/* Caller must make sure id is allocated */
  63int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
  64{
  65        struct expr_id_data *data_ptr = NULL, *old_data = NULL;
  66        char *old_key = NULL;
  67        int ret;
  68
  69        data_ptr = malloc(sizeof(*data_ptr));
  70        if (!data_ptr)
  71                return -ENOMEM;
  72
  73        data_ptr->parent = ctx->parent;
  74        data_ptr->kind = EXPR_ID_DATA__PARENT;
  75
  76        ret = hashmap__set(&ctx->ids, id, data_ptr,
  77                           (const void **)&old_key, (void **)&old_data);
  78        if (ret)
  79                free(data_ptr);
  80        free(old_key);
  81        free(old_data);
  82        return ret;
  83}
  84
  85/* Caller must make sure id is allocated */
  86int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val)
  87{
  88        struct expr_id_data *data_ptr = NULL, *old_data = NULL;
  89        char *old_key = NULL;
  90        int ret;
  91
  92        data_ptr = malloc(sizeof(*data_ptr));
  93        if (!data_ptr)
  94                return -ENOMEM;
  95        data_ptr->val = val;
  96        data_ptr->kind = EXPR_ID_DATA__VALUE;
  97
  98        ret = hashmap__set(&ctx->ids, id, data_ptr,
  99                           (const void **)&old_key, (void **)&old_data);
 100        if (ret)
 101                free(data_ptr);
 102        free(old_key);
 103        free(old_data);
 104        return ret;
 105}
 106
 107int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
 108{
 109        struct expr_id_data *data_ptr = NULL, *old_data = NULL;
 110        char *old_key = NULL;
 111        char *name, *p;
 112        int ret;
 113
 114        data_ptr = zalloc(sizeof(*data_ptr));
 115        if (!data_ptr)
 116                return -ENOMEM;
 117
 118        name = strdup(ref->metric_name);
 119        if (!name) {
 120                free(data_ptr);
 121                return -ENOMEM;
 122        }
 123
 124        /*
 125         * The jevents tool converts all metric expressions
 126         * to lowercase, including metric references, hence
 127         * we need to add lowercase name for metric, so it's
 128         * properly found.
 129         */
 130        for (p = name; *p; p++)
 131                *p = tolower(*p);
 132
 133        /*
 134         * Intentionally passing just const char pointers,
 135         * originally from 'struct pmu_event' object.
 136         * We don't need to change them, so there's no
 137         * need to create our own copy.
 138         */
 139        data_ptr->ref.metric_name = ref->metric_name;
 140        data_ptr->ref.metric_expr = ref->metric_expr;
 141        data_ptr->kind = EXPR_ID_DATA__REF;
 142
 143        ret = hashmap__set(&ctx->ids, name, data_ptr,
 144                           (const void **)&old_key, (void **)&old_data);
 145        if (ret)
 146                free(data_ptr);
 147
 148        pr_debug2("adding ref metric %s: %s\n",
 149                  ref->metric_name, ref->metric_expr);
 150
 151        free(old_key);
 152        free(old_data);
 153        return ret;
 154}
 155
 156int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
 157                 struct expr_id_data **data)
 158{
 159        return hashmap__find(&ctx->ids, id, (void **)data) ? 0 : -1;
 160}
 161
 162int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
 163                     struct expr_id_data **datap)
 164{
 165        struct expr_id_data *data;
 166
 167        if (expr__get_id(ctx, id, datap) || !*datap) {
 168                pr_debug("%s not found\n", id);
 169                return -1;
 170        }
 171
 172        data = *datap;
 173
 174        switch (data->kind) {
 175        case EXPR_ID_DATA__VALUE:
 176                pr_debug2("lookup(%s): val %f\n", id, data->val);
 177                break;
 178        case EXPR_ID_DATA__PARENT:
 179                pr_debug2("lookup(%s): parent %s\n", id, data->parent->id);
 180                break;
 181        case EXPR_ID_DATA__REF:
 182                pr_debug2("lookup(%s): ref metric name %s\n", id,
 183                        data->ref.metric_name);
 184                pr_debug("processing metric: %s ENTRY\n", id);
 185                data->kind = EXPR_ID_DATA__REF_VALUE;
 186                if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr, 1)) {
 187                        pr_debug("%s failed to count\n", id);
 188                        return -1;
 189                }
 190                pr_debug("processing metric: %s EXIT: %f\n", id, data->val);
 191                break;
 192        case EXPR_ID_DATA__REF_VALUE:
 193                pr_debug2("lookup(%s): ref val %f metric name %s\n", id,
 194                        data->ref.val, data->ref.metric_name);
 195                break;
 196        default:
 197                assert(0);  /* Unreachable. */
 198        }
 199
 200        return 0;
 201}
 202
 203void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
 204{
 205        struct expr_id_data *old_val = NULL;
 206        char *old_key = NULL;
 207
 208        hashmap__delete(&ctx->ids, id,
 209                        (const void **)&old_key, (void **)&old_val);
 210        free(old_key);
 211        free(old_val);
 212}
 213
 214void expr__ctx_init(struct expr_parse_ctx *ctx)
 215{
 216        hashmap__init(&ctx->ids, key_hash, key_equal, NULL);
 217}
 218
 219void expr__ctx_clear(struct expr_parse_ctx *ctx)
 220{
 221        struct hashmap_entry *cur;
 222        size_t bkt;
 223
 224        hashmap__for_each_entry((&ctx->ids), cur, bkt) {
 225                free((char *)cur->key);
 226                free(cur->value);
 227        }
 228        hashmap__clear(&ctx->ids);
 229}
 230
 231static int
 232__expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
 233              int start, int runtime)
 234{
 235        struct expr_scanner_ctx scanner_ctx = {
 236                .start_token = start,
 237                .runtime = runtime,
 238        };
 239        YY_BUFFER_STATE buffer;
 240        void *scanner;
 241        int ret;
 242
 243        pr_debug2("parsing metric: %s\n", expr);
 244
 245        ret = expr_lex_init_extra(&scanner_ctx, &scanner);
 246        if (ret)
 247                return ret;
 248
 249        buffer = expr__scan_string(expr, scanner);
 250
 251#ifdef PARSER_DEBUG
 252        expr_debug = 1;
 253        expr_set_debug(1, scanner);
 254#endif
 255
 256        ret = expr_parse(val, ctx, scanner);
 257
 258        expr__flush_buffer(buffer, scanner);
 259        expr__delete_buffer(buffer, scanner);
 260        expr_lex_destroy(scanner);
 261        return ret;
 262}
 263
 264int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
 265                const char *expr, int runtime)
 266{
 267        return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 0;
 268}
 269
 270int expr__find_other(const char *expr, const char *one,
 271                     struct expr_parse_ctx *ctx, int runtime)
 272{
 273        int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
 274
 275        if (one)
 276                expr__del_id(ctx, one);
 277
 278        return ret;
 279}
 280
 281double expr_id_data__value(const struct expr_id_data *data)
 282{
 283        if (data->kind == EXPR_ID_DATA__VALUE)
 284                return data->val;
 285        assert(data->kind == EXPR_ID_DATA__REF_VALUE);
 286        return data->ref.val;
 287}
 288
 289struct expr_id *expr_id_data__parent(struct expr_id_data *data)
 290{
 291        assert(data->kind == EXPR_ID_DATA__PARENT);
 292        return data->parent;
 293}
 294