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 17 extern int expr_debug; 18 #endif 19 20 struct 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 43 static 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 56 static 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 */ 63 int 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 */ 86 int 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 107 int 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 156 int 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 162 int 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 203 void 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 214 void expr__ctx_init(struct expr_parse_ctx *ctx) 215 { 216 hashmap__init(&ctx->ids, key_hash, key_equal, NULL); 217 } 218 219 void 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 231 static 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 264 int 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 270 int 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 281 double 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 289 struct 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