1 /* Simple expression parser */ 1 /* Simple expression parser */ 2 %{ 2 %{ 3 #ifndef NDEBUG << 4 #define YYDEBUG 1 3 #define YYDEBUG 1 5 #endif << 6 #include <assert.h> 4 #include <assert.h> 7 #include <math.h> 5 #include <math.h> 8 #include <stdlib.h> 6 #include <stdlib.h> 9 #include "util/debug.h" 7 #include "util/debug.h" 10 #define IN_EXPR_Y 1 8 #define IN_EXPR_Y 1 11 #include "expr.h" 9 #include "expr.h" 12 #include "expr-bison.h" << 13 int expr_lex(YYSTYPE * yylval_param , void *yy << 14 %} 10 %} 15 11 16 %define api.pure full 12 %define api.pure full 17 13 18 %parse-param { double *final_val } 14 %parse-param { double *final_val } 19 %parse-param { struct expr_parse_ctx *ctx } 15 %parse-param { struct expr_parse_ctx *ctx } 20 %parse-param { bool compute_ids } 16 %parse-param { bool compute_ids } 21 %parse-param {void *scanner} 17 %parse-param {void *scanner} 22 %lex-param {void* scanner} 18 %lex-param {void* scanner} 23 19 24 %union { 20 %union { 25 double num; 21 double num; 26 char *str; 22 char *str; 27 struct ids { 23 struct ids { 28 /* 24 /* 29 * When creating ids, holds th 25 * When creating ids, holds the working set of event ids. NULL 30 * implies the set is empty. 26 * implies the set is empty. 31 */ 27 */ 32 struct hashmap *ids; 28 struct hashmap *ids; 33 /* 29 /* 34 * The metric value. When not 30 * The metric value. When not creating ids this is the value 35 * read from a counter, a cons 31 * read from a counter, a constant or some computed value. When 36 * creating ids the value is e 32 * creating ids the value is either a constant or BOTTOM. NAN is 37 * used as the special BOTTOM 33 * used as the special BOTTOM value, representing a "set of all 38 * values" case. 34 * values" case. 39 */ 35 */ 40 double val; 36 double val; 41 } ids; 37 } ids; 42 } 38 } 43 39 44 %token ID NUMBER MIN MAX IF ELSE LITERAL D_RAT !! 40 %token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT EXPR_ERROR 45 %left MIN MAX IF 41 %left MIN MAX IF 46 %left '|' 42 %left '|' 47 %left '^' 43 %left '^' 48 %left '&' 44 %left '&' 49 %left '<' '>' 45 %left '<' '>' 50 %left '-' '+' 46 %left '-' '+' 51 %left '*' '/' '%' 47 %left '*' '/' '%' 52 %left NEG NOT 48 %left NEG NOT 53 %type <num> NUMBER LITERAL 49 %type <num> NUMBER LITERAL 54 %type <str> ID 50 %type <str> ID 55 %destructor { free ($$); } <str> 51 %destructor { free ($$); } <str> 56 %type <ids> expr if_expr 52 %type <ids> expr if_expr 57 %destructor { ids__free($$.ids); } <ids> 53 %destructor { ids__free($$.ids); } <ids> 58 54 59 %{ 55 %{ 60 static void expr_error(double *final_val __may 56 static void expr_error(double *final_val __maybe_unused, 61 struct expr_parse_ctx * 57 struct expr_parse_ctx *ctx __maybe_unused, 62 bool compute_ids __mayb 58 bool compute_ids __maybe_unused, 63 void *scanner __maybe_u !! 59 void *scanner, 64 const char *s) 60 const char *s) 65 { 61 { 66 pr_debug("%s\n", s); 62 pr_debug("%s\n", s); 67 } 63 } 68 64 69 /* 65 /* 70 * During compute ids, the special "bottom" va 66 * During compute ids, the special "bottom" value uses NAN to represent the set 71 * of all values. NAN is selected as it isn't 67 * of all values. NAN is selected as it isn't a useful constant value. 72 */ 68 */ 73 #define BOTTOM NAN 69 #define BOTTOM NAN 74 70 75 /* During computing ids, does val represent a 71 /* During computing ids, does val represent a constant (non-BOTTOM) value? */ 76 static bool is_const(double val) 72 static bool is_const(double val) 77 { 73 { 78 return isfinite(val); 74 return isfinite(val); 79 } 75 } 80 76 81 static struct ids union_expr(struct ids ids1, 77 static struct ids union_expr(struct ids ids1, struct ids ids2) 82 { 78 { 83 struct ids result = { 79 struct ids result = { 84 .val = BOTTOM, 80 .val = BOTTOM, 85 .ids = ids__union(ids1.ids, id 81 .ids = ids__union(ids1.ids, ids2.ids), 86 }; 82 }; 87 return result; 83 return result; 88 } 84 } 89 85 90 static struct ids handle_id(struct expr_parse_ 86 static struct ids handle_id(struct expr_parse_ctx *ctx, char *id, 91 bool compute_ids, 87 bool compute_ids, bool source_count) 92 { 88 { 93 struct ids result; 89 struct ids result; 94 90 95 if (!compute_ids) { 91 if (!compute_ids) { 96 /* 92 /* 97 * Compute the event's value f 93 * Compute the event's value from ID. If the ID isn't known then 98 * it isn't used to compute th 94 * it isn't used to compute the formula so set to NAN. 99 */ 95 */ 100 struct expr_id_data *data; 96 struct expr_id_data *data; 101 97 102 result.val = NAN; 98 result.val = NAN; 103 if (expr__resolve_id(ctx, id, 99 if (expr__resolve_id(ctx, id, &data) == 0) { 104 result.val = source_co 100 result.val = source_count 105 ? expr_id_data 101 ? expr_id_data__source_count(data) 106 : expr_id_data 102 : expr_id_data__value(data); 107 } 103 } 108 result.ids = NULL; 104 result.ids = NULL; 109 free(id); 105 free(id); 110 } else { 106 } else { 111 /* 107 /* 112 * Set the value to BOTTOM to 108 * Set the value to BOTTOM to show that any value is possible 113 * when the event is computed. 109 * when the event is computed. Create a set of just the ID. 114 */ 110 */ 115 result.val = BOTTOM; 111 result.val = BOTTOM; 116 result.ids = ids__new(); 112 result.ids = ids__new(); 117 if (!result.ids || ids__insert 113 if (!result.ids || ids__insert(result.ids, id)) { 118 pr_err("Error creating 114 pr_err("Error creating IDs for '%s'", id); 119 free(id); 115 free(id); 120 } 116 } 121 } 117 } 122 return result; 118 return result; 123 } 119 } 124 120 125 /* 121 /* 126 * If we're not computing ids or $1 and $3 are 122 * If we're not computing ids or $1 and $3 are constants, compute the new 127 * constant value using OP. Its invariant that 123 * constant value using OP. Its invariant that there are no ids. If computing 128 * ids for non-constants union the set of IDs 124 * ids for non-constants union the set of IDs that must be computed. 129 */ 125 */ >> 126 #define BINARY_LONG_OP(RESULT, OP, LHS, RHS) \ >> 127 if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \ >> 128 assert(LHS.ids == NULL); \ >> 129 assert(RHS.ids == NULL); \ >> 130 if (isnan(LHS.val) || isnan(RHS.val)) { \ >> 131 RESULT.val = NAN; \ >> 132 } else { \ >> 133 RESULT.val = (long)LHS.val OP (long)RHS.val; \ >> 134 } \ >> 135 RESULT.ids = NULL; \ >> 136 } else { \ >> 137 RESULT = union_expr(LHS, RHS); \ >> 138 } >> 139 130 #define BINARY_OP(RESULT, OP, LHS, RHS) 140 #define BINARY_OP(RESULT, OP, LHS, RHS) \ 131 if (!compute_ids || (is_const(LHS.val) 141 if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \ 132 assert(LHS.ids == NULL); 142 assert(LHS.ids == NULL); \ 133 assert(RHS.ids == NULL); 143 assert(RHS.ids == NULL); \ 134 if (isnan(LHS.val) || isnan(RH 144 if (isnan(LHS.val) || isnan(RHS.val)) { \ 135 RESULT.val = NAN; 145 RESULT.val = NAN; \ 136 } else { 146 } else { \ 137 RESULT.val = LHS.val O 147 RESULT.val = LHS.val OP RHS.val; \ 138 } 148 } \ 139 RESULT.ids = NULL; 149 RESULT.ids = NULL; \ 140 } else { 150 } else { \ 141 RESULT = union_expr(LHS, RHS); 151 RESULT = union_expr(LHS, RHS); \ 142 } 152 } 143 153 144 %} 154 %} 145 %% 155 %% 146 156 147 start: if_expr 157 start: if_expr 148 { 158 { 149 if (compute_ids) 159 if (compute_ids) 150 ctx->ids = ids__union($1.ids, 160 ctx->ids = ids__union($1.ids, ctx->ids); 151 161 152 if (final_val) 162 if (final_val) 153 *final_val = $1.val; 163 *final_val = $1.val; 154 } 164 } 155 ; 165 ; 156 166 157 if_expr: expr IF expr ELSE if_expr 167 if_expr: expr IF expr ELSE if_expr 158 { 168 { 159 if (fpclassify($3.val) == FP_ZERO) { 169 if (fpclassify($3.val) == FP_ZERO) { 160 /* 170 /* 161 * The IF expression evaluated 171 * The IF expression evaluated to 0 so treat as false, take the 162 * ELSE and discard everything 172 * ELSE and discard everything else. 163 */ 173 */ 164 $$.val = $5.val; 174 $$.val = $5.val; 165 $$.ids = $5.ids; 175 $$.ids = $5.ids; 166 ids__free($1.ids); 176 ids__free($1.ids); 167 ids__free($3.ids); 177 ids__free($3.ids); 168 } else if (!compute_ids || is_const($3 178 } else if (!compute_ids || is_const($3.val)) { 169 /* 179 /* 170 * If ids aren't computed then 180 * If ids aren't computed then treat the expression as true. If 171 * ids are being computed and 181 * ids are being computed and the IF expr is a non-zero 172 * constant, then also evaluat 182 * constant, then also evaluate the true case. 173 */ 183 */ 174 $$.val = $1.val; 184 $$.val = $1.val; 175 $$.ids = $1.ids; 185 $$.ids = $1.ids; 176 ids__free($3.ids); 186 ids__free($3.ids); 177 ids__free($5.ids); 187 ids__free($5.ids); 178 } else if ($1.val == $5.val) { 188 } else if ($1.val == $5.val) { 179 /* 189 /* 180 * LHS == RHS, so both are an 190 * LHS == RHS, so both are an identical constant. No need to 181 * evaluate any events. 191 * evaluate any events. 182 */ 192 */ 183 $$.val = $1.val; 193 $$.val = $1.val; 184 $$.ids = NULL; 194 $$.ids = NULL; 185 ids__free($1.ids); 195 ids__free($1.ids); 186 ids__free($3.ids); 196 ids__free($3.ids); 187 ids__free($5.ids); 197 ids__free($5.ids); 188 } else { 198 } else { 189 /* 199 /* 190 * Value is either the LHS or 200 * Value is either the LHS or RHS and we need the IF expression 191 * to compute it. 201 * to compute it. 192 */ 202 */ 193 $$ = union_expr($1, union_expr 203 $$ = union_expr($1, union_expr($3, $5)); 194 } 204 } 195 } 205 } 196 | expr 206 | expr 197 ; 207 ; 198 208 199 expr: NUMBER 209 expr: NUMBER 200 { 210 { 201 $$.val = $1; 211 $$.val = $1; 202 $$.ids = NULL; 212 $$.ids = NULL; 203 } 213 } 204 | ID { $$ = handle_ 214 | ID { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); } 205 | SOURCE_COUNT '(' ID ')' { $$ = handle_ 215 | SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); } 206 | HAS_EVENT '(' ID ')' !! 216 | expr '|' expr { BINARY_LONG_OP($$, |, $1, $3); } 207 { !! 217 | expr '&' expr { BINARY_LONG_OP($$, &, $1, $3); } 208 $$.val = expr__has_event(ctx, compute_ !! 218 | expr '^' expr { BINARY_LONG_OP($$, ^, $1, $3); } 209 $$.ids = NULL; << 210 free($3); << 211 } << 212 | STRCMP_CPUID_STR '(' ID ')' << 213 { << 214 $$.val = expr__strcmp_cpuid_str(ctx, c << 215 $$.ids = NULL; << 216 free($3); << 217 } << 218 | expr '|' expr << 219 { << 220 if (is_const($1.val) && is_const($3.va << 221 assert($1.ids == NULL); << 222 assert($3.ids == NULL); << 223 $$.ids = NULL; << 224 $$.val = (fpclassify($1.val) = << 225 } else if (is_const($1.val)) { << 226 assert($1.ids == NULL); << 227 if (fpclassify($1.val) == FP_Z << 228 $$ = $3; << 229 } else { << 230 $$.val = 1; << 231 $$.ids = NULL; << 232 ids__free($3.ids); << 233 } << 234 } else if (is_const($3.val)) { << 235 assert($3.ids == NULL); << 236 if (fpclassify($3.val) == FP_Z << 237 $$ = $1; << 238 } else { << 239 $$.val = 1; << 240 $$.ids = NULL; << 241 ids__free($1.ids); << 242 } << 243 } else { << 244 $$ = union_expr($1, $3); << 245 } << 246 } << 247 | expr '&' expr << 248 { << 249 if (is_const($1.val) && is_const($3.va << 250 assert($1.ids == NULL); << 251 assert($3.ids == NULL); << 252 $$.val = (fpclassify($1.val) ! << 253 $$.ids = NULL; << 254 } else if (is_const($1.val)) { << 255 assert($1.ids == NULL); << 256 if (fpclassify($1.val) != FP_Z << 257 $$ = $3; << 258 } else { << 259 $$.val = 0; << 260 $$.ids = NULL; << 261 ids__free($3.ids); << 262 } << 263 } else if (is_const($3.val)) { << 264 assert($3.ids == NULL); << 265 if (fpclassify($3.val) != FP_Z << 266 $$ = $1; << 267 } else { << 268 $$.val = 0; << 269 $$.ids = NULL; << 270 ids__free($1.ids); << 271 } << 272 } else { << 273 $$ = union_expr($1, $3); << 274 } << 275 } << 276 | expr '^' expr << 277 { << 278 if (is_const($1.val) && is_const($3.va << 279 assert($1.ids == NULL); << 280 assert($3.ids == NULL); << 281 $$.val = (fpclassify($1.val) = << 282 $$.ids = NULL; << 283 } else { << 284 $$ = union_expr($1, $3); << 285 } << 286 } << 287 | expr '<' expr { BINARY_OP($$, <, $1, $3); } 219 | expr '<' expr { BINARY_OP($$, <, $1, $3); } 288 | expr '>' expr { BINARY_OP($$, >, $1, $3); } 220 | expr '>' expr { BINARY_OP($$, >, $1, $3); } 289 | expr '+' expr { BINARY_OP($$, +, $1, $3); } 221 | expr '+' expr { BINARY_OP($$, +, $1, $3); } 290 | expr '-' expr { BINARY_OP($$, -, $1, $3); } 222 | expr '-' expr { BINARY_OP($$, -, $1, $3); } 291 | expr '*' expr { BINARY_OP($$, *, $1, $3); } 223 | expr '*' expr { BINARY_OP($$, *, $1, $3); } 292 | expr '/' expr 224 | expr '/' expr 293 { 225 { 294 if (fpclassify($3.val) == FP_ZERO) { 226 if (fpclassify($3.val) == FP_ZERO) { 295 pr_debug("division by zero\n") 227 pr_debug("division by zero\n"); 296 assert($3.ids == NULL); 228 assert($3.ids == NULL); 297 if (compute_ids) 229 if (compute_ids) 298 ids__free($1.ids); 230 ids__free($1.ids); 299 $$.val = NAN; 231 $$.val = NAN; 300 $$.ids = NULL; 232 $$.ids = NULL; 301 } else if (!compute_ids || (is_const($ 233 } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) { 302 assert($1.ids == NULL); 234 assert($1.ids == NULL); 303 assert($3.ids == NULL); 235 assert($3.ids == NULL); 304 $$.val = $1.val / $3.val; 236 $$.val = $1.val / $3.val; 305 $$.ids = NULL; 237 $$.ids = NULL; 306 } else { 238 } else { 307 /* LHS and/or RHS need computi 239 /* LHS and/or RHS need computing from event IDs so union. */ 308 $$ = union_expr($1, $3); 240 $$ = union_expr($1, $3); 309 } 241 } 310 } 242 } 311 | expr '%' expr 243 | expr '%' expr 312 { 244 { 313 if (fpclassify($3.val) == FP_ZERO) { 245 if (fpclassify($3.val) == FP_ZERO) { 314 pr_debug("division by zero\n") 246 pr_debug("division by zero\n"); 315 YYABORT; 247 YYABORT; 316 } else if (!compute_ids || (is_const($ 248 } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) { 317 assert($1.ids == NULL); 249 assert($1.ids == NULL); 318 assert($3.ids == NULL); 250 assert($3.ids == NULL); 319 $$.val = (long)$1.val % (long) 251 $$.val = (long)$1.val % (long)$3.val; 320 $$.ids = NULL; 252 $$.ids = NULL; 321 } else { 253 } else { 322 /* LHS and/or RHS need computi 254 /* LHS and/or RHS need computing from event IDs so union. */ 323 $$ = union_expr($1, $3); 255 $$ = union_expr($1, $3); 324 } 256 } 325 } 257 } 326 | D_RATIO '(' expr ',' expr ')' 258 | D_RATIO '(' expr ',' expr ')' 327 { 259 { 328 if (fpclassify($5.val) == FP_ZERO) { 260 if (fpclassify($5.val) == FP_ZERO) { 329 /* 261 /* 330 * Division by constant zero a 262 * Division by constant zero always yields zero and no events 331 * are necessary. 263 * are necessary. 332 */ 264 */ 333 assert($5.ids == NULL); 265 assert($5.ids == NULL); 334 $$.val = 0.0; 266 $$.val = 0.0; 335 $$.ids = NULL; 267 $$.ids = NULL; 336 ids__free($3.ids); 268 ids__free($3.ids); 337 } else if (!compute_ids || (is_const($ 269 } else if (!compute_ids || (is_const($3.val) && is_const($5.val))) { 338 assert($3.ids == NULL); 270 assert($3.ids == NULL); 339 assert($5.ids == NULL); 271 assert($5.ids == NULL); 340 $$.val = $3.val / $5.val; 272 $$.val = $3.val / $5.val; 341 $$.ids = NULL; 273 $$.ids = NULL; 342 } else { 274 } else { 343 /* LHS and/or RHS need computi 275 /* LHS and/or RHS need computing from event IDs so union. */ 344 $$ = union_expr($3, $5); 276 $$ = union_expr($3, $5); 345 } 277 } 346 } 278 } 347 | '-' expr %prec NEG 279 | '-' expr %prec NEG 348 { 280 { 349 $$.val = -$2.val; 281 $$.val = -$2.val; 350 $$.ids = $2.ids; 282 $$.ids = $2.ids; 351 } 283 } 352 | '(' if_expr ')' 284 | '(' if_expr ')' 353 { 285 { 354 $$ = $2; 286 $$ = $2; 355 } 287 } 356 | MIN '(' expr ',' expr ')' 288 | MIN '(' expr ',' expr ')' 357 { 289 { 358 if (!compute_ids) { 290 if (!compute_ids) { 359 $$.val = $3.val < $5.val ? $3. 291 $$.val = $3.val < $5.val ? $3.val : $5.val; 360 $$.ids = NULL; 292 $$.ids = NULL; 361 } else { 293 } else { 362 $$ = union_expr($3, $5); 294 $$ = union_expr($3, $5); 363 } 295 } 364 } 296 } 365 | MAX '(' expr ',' expr ')' 297 | MAX '(' expr ',' expr ')' 366 { 298 { 367 if (!compute_ids) { 299 if (!compute_ids) { 368 $$.val = $3.val > $5.val ? $3. 300 $$.val = $3.val > $5.val ? $3.val : $5.val; 369 $$.ids = NULL; 301 $$.ids = NULL; 370 } else { 302 } else { 371 $$ = union_expr($3, $5); 303 $$ = union_expr($3, $5); 372 } 304 } 373 } 305 } 374 | LITERAL 306 | LITERAL 375 { 307 { 376 $$.val = $1; 308 $$.val = $1; 377 $$.ids = NULL; 309 $$.ids = NULL; 378 } 310 } 379 ; 311 ; 380 312 381 %% 313 %%
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.