diff options
| author | Steven Rostedt <srostedt@redhat.com> | 2010-02-22 21:30:09 -0500 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2010-02-22 21:30:09 -0500 |
| commit | ad3eec12fc6035b49177f09d15b2f9fb466fe1fa (patch) | |
| tree | 428b0b08a5dc19c0248c6207bcf23cbbbb373c14 | |
| parent | 855f4eeaa3dd370145c737ad8303c5b5abc029ea (diff) | |
parse-events: Restructure advanced filter to handle arithmetic
Have fields be compared to each other and have mathmatic
algorithms applied.
Now we can do:
funcgraph_exit : rettime - calltime > 1000000
Or
sched_wakeup : pid == common_pid
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
| -rw-r--r-- | parse-events.h | 41 | ||||
| -rw-r--r-- | parse-filter.c | 779 |
2 files changed, 620 insertions, 200 deletions
diff --git a/parse-events.h b/parse-events.h index bccb9ee..f87d85c 100644 --- a/parse-events.h +++ b/parse-events.h | |||
| @@ -524,20 +524,55 @@ enum filter_cmp_type { | |||
| 524 | FILTER_CMP_NOT_REGEX, | 524 | FILTER_CMP_NOT_REGEX, |
| 525 | }; | 525 | }; |
| 526 | 526 | ||
| 527 | enum filter_exp_type { | ||
| 528 | FILTER_EXP_NONE, | ||
| 529 | FILTER_EXP_ADD, | ||
| 530 | FILTER_EXP_SUB, | ||
| 531 | FILTER_EXP_MUL, | ||
| 532 | FILTER_EXP_DIV, | ||
| 533 | FILTER_EXP_MOD, | ||
| 534 | FILTER_EXP_RSHIFT, | ||
| 535 | FILTER_EXP_LSHIFT, | ||
| 536 | FILTER_EXP_AND, | ||
| 537 | FILTER_EXP_OR, | ||
| 538 | FILTER_EXP_XOR, | ||
| 539 | FILTER_EXP_NOT, | ||
| 540 | }; | ||
| 541 | |||
| 527 | enum filter_arg_type { | 542 | enum filter_arg_type { |
| 528 | FILTER_ARG_NONE, | 543 | FILTER_ARG_NONE, |
| 529 | FILTER_ARG_BOOLEAN, | 544 | FILTER_ARG_BOOLEAN, |
| 545 | FILTER_ARG_VALUE, | ||
| 546 | FILTER_ARG_FIELD, | ||
| 530 | FILTER_ARG_OP, | 547 | FILTER_ARG_OP, |
| 548 | FILTER_ARG_EXP, | ||
| 531 | FILTER_ARG_NUM, | 549 | FILTER_ARG_NUM, |
| 532 | FILTER_ARG_STR, | 550 | FILTER_ARG_STR, |
| 533 | }; | 551 | }; |
| 534 | 552 | ||
| 553 | enum filter_value_type { | ||
| 554 | FILTER_NUMBER, | ||
| 555 | FILTER_STRING | ||
| 556 | }; | ||
| 557 | |||
| 535 | struct fliter_arg; | 558 | struct fliter_arg; |
| 536 | 559 | ||
| 537 | struct filter_arg_boolean { | 560 | struct filter_arg_boolean { |
| 538 | enum filter_boolean_type value; | 561 | enum filter_boolean_type value; |
| 539 | }; | 562 | }; |
| 540 | 563 | ||
| 564 | struct filter_arg_field { | ||
| 565 | struct format_field *field; | ||
| 566 | }; | ||
| 567 | |||
| 568 | struct filter_arg_value { | ||
| 569 | enum filter_value_type type; | ||
| 570 | union { | ||
| 571 | char *str; | ||
| 572 | unsigned long long val; | ||
| 573 | }; | ||
| 574 | }; | ||
| 575 | |||
| 541 | struct filter_arg_op { | 576 | struct filter_arg_op { |
| 542 | enum filter_op_type type; | 577 | enum filter_op_type type; |
| 543 | struct filter_arg *left; | 578 | struct filter_arg *left; |
| @@ -546,8 +581,8 @@ struct filter_arg_op { | |||
| 546 | 581 | ||
| 547 | struct filter_arg_num { | 582 | struct filter_arg_num { |
| 548 | enum filter_cmp_type type; | 583 | enum filter_cmp_type type; |
| 549 | struct format_field *field; | 584 | struct filter_arg *left; |
| 550 | unsigned long long val; | 585 | struct filter_arg *right; |
| 551 | }; | 586 | }; |
| 552 | 587 | ||
| 553 | struct filter_arg_str { | 588 | struct filter_arg_str { |
| @@ -562,6 +597,8 @@ struct filter_arg { | |||
| 562 | enum filter_arg_type type; | 597 | enum filter_arg_type type; |
| 563 | union { | 598 | union { |
| 564 | struct filter_arg_boolean bool; | 599 | struct filter_arg_boolean bool; |
| 600 | struct filter_arg_field field; | ||
| 601 | struct filter_arg_value value; | ||
| 565 | struct filter_arg_op op; | 602 | struct filter_arg_op op; |
| 566 | struct filter_arg_num num; | 603 | struct filter_arg_num num; |
| 567 | struct filter_arg_str str; | 604 | struct filter_arg_str str; |
diff --git a/parse-filter.c b/parse-filter.c index 816582d..0890589 100644 --- a/parse-filter.c +++ b/parse-filter.c | |||
| @@ -297,99 +297,35 @@ static void free_events(struct event_list *events) | |||
| 297 | } | 297 | } |
| 298 | } | 298 | } |
| 299 | 299 | ||
| 300 | static int process_valid_field(struct filter_arg *arg, | ||
| 301 | struct format_field *field, | ||
| 302 | enum filter_cmp_type op_type, | ||
| 303 | enum event_type type, | ||
| 304 | char *val, | ||
| 305 | char **error_str) | ||
| 306 | { | ||
| 307 | int ret; | ||
| 308 | |||
| 309 | switch (type) { | ||
| 310 | |||
| 311 | case EVENT_SQUOTE: | ||
| 312 | /* treat this as a character if string is of length 1? */ | ||
| 313 | if (strlen(val) == 1) | ||
| 314 | goto as_int; | ||
| 315 | /* fall through */ | ||
| 316 | |||
| 317 | case EVENT_DQUOTE: | ||
| 318 | /* right now only allow match */ | ||
| 319 | switch (op_type) { | ||
| 320 | case FILTER_CMP_EQ: | ||
| 321 | op_type = FILTER_CMP_MATCH; | ||
| 322 | break; | ||
| 323 | case FILTER_CMP_NE: | ||
| 324 | op_type = FILTER_CMP_NOT_MATCH; | ||
| 325 | break; | ||
| 326 | |||
| 327 | case FILTER_CMP_REGEX: | ||
| 328 | case FILTER_CMP_NOT_REGEX: | ||
| 329 | ret = regcomp(&arg->str.reg, val, REG_ICASE|REG_NOSUB); | ||
| 330 | if (ret) | ||
| 331 | return -1; | ||
| 332 | break; | ||
| 333 | |||
| 334 | default: | ||
| 335 | show_error(error_str, | ||
| 336 | "Op not allowed with string"); | ||
| 337 | return -1; | ||
| 338 | } | ||
| 339 | arg->type = FILTER_ARG_STR; | ||
| 340 | arg->str.field = field; | ||
| 341 | arg->str.type = op_type; | ||
| 342 | arg->str.val = strdup(val); | ||
| 343 | if (!arg->str.val) | ||
| 344 | die("Can't allocate arg value"); | ||
| 345 | |||
| 346 | /* Need a buffer to copy data int for tests */ | ||
| 347 | arg->str.buffer = malloc_or_die(arg->str.field->size + 1); | ||
| 348 | /* Null terminate this buffer */ | ||
| 349 | arg->str.buffer[arg->str.field->size] = 0; | ||
| 350 | |||
| 351 | break; | ||
| 352 | case EVENT_ITEM: | ||
| 353 | as_int: | ||
| 354 | switch (op_type) { | ||
| 355 | case FILTER_CMP_REGEX: | ||
| 356 | case FILTER_CMP_NOT_REGEX: | ||
| 357 | show_error(error_str, | ||
| 358 | "Op not allowed with integers"); | ||
| 359 | return -1; | ||
| 360 | default: | ||
| 361 | break; | ||
| 362 | } | ||
| 363 | arg->type = FILTER_ARG_NUM; | ||
| 364 | arg->num.field = field; | ||
| 365 | arg->num.type = op_type; | ||
| 366 | arg->num.val = strtoll(val, NULL, 0); | ||
| 367 | break; | ||
| 368 | |||
| 369 | default: | ||
| 370 | /* Can't happen */ | ||
| 371 | return -1; | ||
| 372 | } | ||
| 373 | |||
| 374 | return 0; | ||
| 375 | } | ||
| 376 | |||
| 377 | static enum event_type | 300 | static enum event_type |
| 378 | process_filter(struct event_format *event, struct filter_arg **parg, | 301 | process_filter(struct event_format *event, struct filter_arg **parg, |
| 379 | char **tok, char **error_str, int cont); | 302 | enum event_type type, char **tok, char **error_str); |
| 380 | 303 | ||
| 381 | static enum event_type | 304 | static enum event_type |
| 382 | process_paren(struct event_format *event, struct filter_arg **parg, | 305 | process_paren(struct event_format *event, struct filter_arg **parg, |
| 383 | char **tok, char **error_str, int cont); | 306 | char **tok, char **error_str); |
| 384 | 307 | ||
| 385 | static enum event_type | 308 | static enum event_type |
| 386 | process_not(struct event_format *event, struct filter_arg **parg, | 309 | process_not(struct event_format *event, struct filter_arg **parg, |
| 387 | char **tok, char **error_str, int cont); | 310 | char **tok, char **error_str); |
| 311 | |||
| 312 | static enum event_type | ||
| 313 | process_op_token(struct event_format *event, struct filter_arg *larg, | ||
| 314 | struct filter_arg **parg, enum event_type type, char **tok, | ||
| 315 | char **error_str); | ||
| 388 | 316 | ||
| 389 | static enum event_type | 317 | static enum event_type |
| 318 | process_op(struct event_format *event, struct filter_arg *larg, | ||
| 319 | struct filter_arg **parg, char **tok, char **error_str); | ||
| 320 | |||
| 321 | /* | ||
| 322 | * Output: tok, parg | ||
| 323 | */ | ||
| 324 | static enum event_type | ||
| 390 | process_token(struct event_format *event, struct filter_arg **parg, | 325 | process_token(struct event_format *event, struct filter_arg **parg, |
| 391 | char **tok, char **error_str, int cont) | 326 | char **tok, char **error_str, int cont) |
| 392 | { | 327 | { |
| 328 | struct filter_arg *arg; | ||
| 393 | enum event_type type; | 329 | enum event_type type; |
| 394 | char *token; | 330 | char *token; |
| 395 | 331 | ||
| @@ -399,168 +335,450 @@ process_token(struct event_format *event, struct filter_arg **parg, | |||
| 399 | type = read_token(&token); | 335 | type = read_token(&token); |
| 400 | 336 | ||
| 401 | if (type == EVENT_ITEM) { | 337 | if (type == EVENT_ITEM) { |
| 402 | type = process_filter(event, parg, &token, error_str, cont); | 338 | type = process_filter(event, parg, type, &token, error_str); |
| 403 | 339 | ||
| 404 | } else if (type == EVENT_DELIM && strcmp(token, "(") == 0) { | 340 | } else if (type == EVENT_DELIM && strcmp(token, "(") == 0) { |
| 405 | free_token(token); | 341 | free_token(token); |
| 406 | type = process_paren(event, parg, &token, error_str, cont); | 342 | type = process_paren(event, parg, &token, error_str); |
| 407 | 343 | ||
| 408 | } else if (type == EVENT_OP && strcmp(token, "!") == 0) { | 344 | } else if (type == EVENT_OP && strcmp(token, "!") == 0) { |
| 409 | type = process_not(event, parg, &token, error_str, cont); | 345 | type = process_not(event, parg, &token, error_str); |
| 346 | } else { | ||
| 347 | if (type == EVENT_NONE) | ||
| 348 | show_error(error_str, "unexpected end of filter"); | ||
| 349 | else | ||
| 350 | show_error(error_str, "unexpected token '%s'", token); | ||
| 351 | type = EVENT_ERROR; | ||
| 352 | } | ||
| 353 | |||
| 354 | *tok = token; | ||
| 355 | while (cont && type != EVENT_ERROR) { | ||
| 356 | if (type != EVENT_OP) | ||
| 357 | break; | ||
| 358 | /* continue */ | ||
| 359 | arg = *parg; | ||
| 360 | *parg = NULL; | ||
| 361 | type = process_op_token(event, arg, parg, type, tok, error_str); | ||
| 410 | } | 362 | } |
| 411 | 363 | ||
| 412 | if (type == EVENT_ERROR) { | 364 | if (type == EVENT_ERROR) { |
| 413 | free_token(token); | ||
| 414 | free_arg(*parg); | 365 | free_arg(*parg); |
| 415 | *parg = NULL; | 366 | *parg = NULL; |
| 416 | return EVENT_ERROR; | 367 | return EVENT_ERROR; |
| 417 | } | 368 | } |
| 418 | 369 | ||
| 419 | *tok = token; | ||
| 420 | return type; | 370 | return type; |
| 421 | } | 371 | } |
| 422 | 372 | ||
| 373 | /* | ||
| 374 | * Input: tok | ||
| 375 | * Output: parg, tok | ||
| 376 | */ | ||
| 423 | static enum event_type | 377 | static enum event_type |
| 424 | process_op(struct event_format *event, struct filter_arg *larg, | 378 | process_bool(struct event_format *event, struct filter_arg *larg, |
| 425 | struct filter_arg **parg, char **tok, char **error_str) | 379 | struct filter_arg **parg, char **tok, char **error_str) |
| 426 | { | 380 | { |
| 427 | enum event_type type; | 381 | struct filter_arg *rarg; |
| 428 | struct filter_arg *arg; | 382 | struct filter_arg *arg; |
| 383 | enum event_type type; | ||
| 384 | enum filter_op_type btype; | ||
| 385 | |||
| 386 | /* Can only be called with '&&' or '||' */ | ||
| 387 | btype = strcmp(*tok, "&&") == 0 ? | ||
| 388 | FILTER_OP_AND : FILTER_OP_OR; | ||
| 389 | |||
| 390 | type = process_token(event, &rarg, tok, error_str, 0); | ||
| 391 | if (type == EVENT_ERROR) { | ||
| 392 | free_arg(larg); | ||
| 393 | *parg = NULL; | ||
| 394 | return type; | ||
| 395 | } | ||
| 396 | |||
| 397 | /* | ||
| 398 | * If larg or rarg is null then if this is AND, the whole expression | ||
| 399 | * becomes NULL, elso if this is an OR, then we use the non NULL | ||
| 400 | * condition. | ||
| 401 | */ | ||
| 402 | if (!larg || !rarg) { | ||
| 403 | if (btype == FILTER_OP_AND || | ||
| 404 | (!larg && !rarg)) { | ||
| 405 | free_arg(larg); | ||
| 406 | free_arg(rarg); | ||
| 407 | *parg = NULL; | ||
| 408 | return type; | ||
| 409 | } | ||
| 410 | *parg = larg ? larg : rarg; | ||
| 411 | return type; | ||
| 412 | } | ||
| 429 | 413 | ||
| 430 | arg = allocate_arg(); | 414 | arg = allocate_arg(); |
| 431 | arg->type = FILTER_ARG_OP; | 415 | arg->type = FILTER_ARG_OP; |
| 416 | arg->op.type = btype; | ||
| 432 | arg->op.left = larg; | 417 | arg->op.left = larg; |
| 418 | arg->op.right = rarg; | ||
| 433 | 419 | ||
| 434 | /* Can only be called with '&&' or '||' */ | 420 | *parg = arg; |
| 435 | arg->op.type = strcmp(*tok, "&&") == 0 ? | ||
| 436 | FILTER_OP_AND : FILTER_OP_OR; | ||
| 437 | 421 | ||
| 438 | free_token(*tok); | 422 | return type; |
| 423 | } | ||
| 439 | 424 | ||
| 440 | type = process_token(event, &arg->op.right, tok, error_str, 1); | 425 | /* |
| 441 | if (type == EVENT_ERROR) | 426 | * Input: tok |
| 427 | * Output: parg | ||
| 428 | */ | ||
| 429 | static enum event_type | ||
| 430 | process_value_token(struct event_format *event, struct filter_arg **parg, | ||
| 431 | enum event_type type, char **tok, char **error_str) | ||
| 432 | { | ||
| 433 | struct format_field *field; | ||
| 434 | struct filter_arg *arg; | ||
| 435 | char *token; | ||
| 436 | |||
| 437 | token = *tok; | ||
| 438 | *tok = NULL; | ||
| 439 | |||
| 440 | arg = allocate_arg(); | ||
| 441 | |||
| 442 | switch (type) { | ||
| 443 | |||
| 444 | case EVENT_SQUOTE: | ||
| 445 | case EVENT_DQUOTE: | ||
| 446 | arg->type = FILTER_ARG_VALUE; | ||
| 447 | arg->value.type = FILTER_STRING; | ||
| 448 | arg->value.str = token; | ||
| 449 | break; | ||
| 450 | case EVENT_ITEM: | ||
| 451 | /* if it is a number, then convert it */ | ||
| 452 | if (isdigit(token[0])) { | ||
| 453 | arg->type = FILTER_ARG_VALUE; | ||
| 454 | arg->value.type = FILTER_NUMBER; | ||
| 455 | arg->value.val = strtoll(token, NULL, 0); | ||
| 456 | free_token(token); | ||
| 457 | break; | ||
| 458 | } | ||
| 459 | /* Consider this a field */ | ||
| 460 | field = pevent_find_any_field(event, token); | ||
| 461 | free_token(token); | ||
| 462 | if (!field) { | ||
| 463 | /* not a field, so NULL it up */ | ||
| 464 | free_arg(arg); | ||
| 465 | arg = NULL; | ||
| 466 | break; | ||
| 467 | } | ||
| 468 | |||
| 469 | arg->type = FILTER_ARG_FIELD; | ||
| 470 | arg->field.field = field; | ||
| 471 | break; | ||
| 472 | default: | ||
| 442 | free_arg(arg); | 473 | free_arg(arg); |
| 474 | show_error(error_str, "expected a value but found %s", | ||
| 475 | token); | ||
| 476 | free_token(token); | ||
| 477 | return EVENT_ERROR; | ||
| 478 | } | ||
| 443 | 479 | ||
| 444 | *parg = arg; | 480 | *parg = arg; |
| 481 | return type; | ||
| 482 | } | ||
| 445 | 483 | ||
| 484 | /* | ||
| 485 | * Output: parg | ||
| 486 | */ | ||
| 487 | static enum event_type | ||
| 488 | process_value(struct event_format *event, struct filter_arg **parg, | ||
| 489 | char **error_str) | ||
| 490 | { | ||
| 491 | enum event_type type; | ||
| 492 | char *token; | ||
| 493 | |||
| 494 | type = read_token(&token); | ||
| 495 | type = process_value_token(event, parg, type, &token, error_str); | ||
| 496 | free_token(token); | ||
| 446 | return type; | 497 | return type; |
| 447 | } | 498 | } |
| 448 | 499 | ||
| 500 | /* | ||
| 501 | * Output: parg, tok | ||
| 502 | */ | ||
| 449 | static enum event_type | 503 | static enum event_type |
| 450 | process_filter(struct event_format *event, struct filter_arg **parg, | 504 | process_cmp(struct event_format *event, enum filter_cmp_type op_type, |
| 451 | char **tok, char **error_str, int cont) | 505 | struct filter_arg *larg, struct filter_arg **parg, |
| 506 | char **tok, char **error_str) | ||
| 452 | { | 507 | { |
| 453 | struct format_field *field; | ||
| 454 | enum filter_cmp_type etype; | ||
| 455 | struct filter_arg *arg; | 508 | struct filter_arg *arg; |
| 509 | struct filter_arg *rarg; | ||
| 456 | enum event_type type; | 510 | enum event_type type; |
| 457 | char *field_name; | ||
| 458 | char *token; | ||
| 459 | char *op; | ||
| 460 | int ret; | 511 | int ret; |
| 461 | 512 | ||
| 462 | *parg = NULL; | 513 | *parg = NULL; |
| 463 | 514 | ||
| 464 | field_name = *tok; | 515 | type = process_value(event, &rarg, error_str); |
| 465 | *tok = NULL; | 516 | if (type == EVENT_ERROR) |
| 517 | return type; | ||
| 466 | 518 | ||
| 467 | type = read_token(&token); | 519 | arg = allocate_arg(); |
| 468 | if (type != EVENT_OP) { | 520 | /* |
| 469 | if (type == EVENT_NONE) | 521 | * If either arg is NULL if right was field not found. |
| 522 | * Then make the entire expression NULL. (will turn to FALSE) | ||
| 523 | */ | ||
| 524 | if (!larg || !rarg) { | ||
| 525 | free_arg(larg); | ||
| 526 | free_arg(rarg); | ||
| 527 | free_arg(arg); | ||
| 528 | arg = NULL; | ||
| 529 | goto cont; | ||
| 530 | } | ||
| 531 | |||
| 532 | switch (type) { | ||
| 533 | case EVENT_SQUOTE: | ||
| 534 | /* treat this as a character if string is of length 1? */ | ||
| 535 | if (strlen(rarg->str.val) == 1) { | ||
| 536 | switch (op_type) { | ||
| 537 | case FILTER_CMP_REGEX: | ||
| 538 | case FILTER_CMP_NOT_REGEX: | ||
| 539 | /* regex can't be used with ints */ | ||
| 540 | break; | ||
| 541 | default: | ||
| 542 | goto as_int; | ||
| 543 | } | ||
| 544 | } | ||
| 545 | /* fall through */ | ||
| 546 | case EVENT_DQUOTE: | ||
| 547 | arg->type = FILTER_ARG_STR; | ||
| 548 | |||
| 549 | if (larg->type != FILTER_ARG_FIELD) { | ||
| 550 | free(larg); | ||
| 551 | free(rarg); | ||
| 470 | show_error(error_str, | 552 | show_error(error_str, |
| 471 | "Expected OP but found end of filter after %s", | 553 | "Illegal lval for string comparison"); |
| 472 | field_name); | 554 | free_arg(arg); |
| 473 | else | 555 | return EVENT_ERROR; |
| 556 | } | ||
| 557 | |||
| 558 | arg->str.field = larg->field.field; | ||
| 559 | free_arg(larg); | ||
| 560 | |||
| 561 | /* free the rarg, and use its token */ | ||
| 562 | arg->str.val = rarg->value.str; | ||
| 563 | rarg->value.str = NULL; | ||
| 564 | free_arg(rarg); | ||
| 565 | |||
| 566 | /* Make sure this is a valid string compare */ | ||
| 567 | switch (op_type) { | ||
| 568 | case FILTER_CMP_EQ: | ||
| 569 | op_type = FILTER_CMP_MATCH; | ||
| 570 | break; | ||
| 571 | case FILTER_CMP_NE: | ||
| 572 | op_type = FILTER_CMP_NOT_MATCH; | ||
| 573 | break; | ||
| 574 | |||
| 575 | case FILTER_CMP_REGEX: | ||
| 576 | case FILTER_CMP_NOT_REGEX: | ||
| 577 | ret = regcomp(&arg->str.reg, arg->str.val, REG_ICASE|REG_NOSUB); | ||
| 578 | if (ret) { | ||
| 579 | show_error(error_str, | ||
| 580 | "RegEx '%s' did not compute", | ||
| 581 | arg->str.val); | ||
| 582 | free_arg(arg); | ||
| 583 | return EVENT_ERROR; | ||
| 584 | } | ||
| 585 | break; | ||
| 586 | default: | ||
| 474 | show_error(error_str, | 587 | show_error(error_str, |
| 475 | "Expected OP but found %s after %s", | 588 | "Illegal comparison for string"); |
| 476 | token, field_name); | 589 | free_arg(arg); |
| 477 | free_token(field_name); | 590 | return EVENT_ERROR; |
| 591 | } | ||
| 592 | |||
| 593 | arg->str.type = op_type; | ||
| 594 | |||
| 595 | /* | ||
| 596 | * Need a buffer to copy data int for tests */ | ||
| 597 | arg->str.buffer = malloc_or_die(arg->str.field->size + 1); | ||
| 598 | /* Null terminate this buffer */ | ||
| 599 | arg->str.buffer[arg->str.field->size] = 0; | ||
| 600 | |||
| 601 | break; | ||
| 602 | default: | ||
| 603 | as_int: | ||
| 604 | switch (op_type) { | ||
| 605 | case FILTER_CMP_REGEX: | ||
| 606 | case FILTER_CMP_NOT_REGEX: | ||
| 607 | show_error(error_str, | ||
| 608 | "Op not allowed with integers"); | ||
| 609 | free_arg(arg); | ||
| 610 | return EVENT_ERROR; | ||
| 611 | default: | ||
| 612 | break; | ||
| 613 | } | ||
| 614 | /* numeric compare */ | ||
| 615 | arg->type = FILTER_ARG_NUM; | ||
| 616 | arg->num.type = op_type; | ||
| 617 | arg->num.left = larg; | ||
| 618 | arg->num.right = rarg; | ||
| 619 | break; | ||
| 620 | } | ||
| 621 | cont: | ||
| 622 | *parg = arg; | ||
| 623 | return read_token(tok); | ||
| 624 | } | ||
| 625 | |||
| 626 | /* | ||
| 627 | * Output: parg, tok | ||
| 628 | */ | ||
| 629 | static enum event_type | ||
| 630 | process_exp(struct event_format *event, enum filter_exp_type etype, | ||
| 631 | struct filter_arg *larg, struct filter_arg **parg, | ||
| 632 | char **tok, char **error_str) | ||
| 633 | { | ||
| 634 | struct filter_arg *rarg; | ||
| 635 | struct filter_arg *arg; | ||
| 636 | enum event_type type; | ||
| 637 | |||
| 638 | type = process_value(event, &rarg, error_str); | ||
| 639 | if (type == EVENT_ERROR) | ||
| 640 | return type; | ||
| 641 | |||
| 642 | /* larg can be NULL if a field did not match */ | ||
| 643 | if (!larg) { | ||
| 644 | /* syntax is correct, just return NULL */ | ||
| 645 | arg = NULL; | ||
| 646 | free_arg(rarg); | ||
| 647 | goto cont; | ||
| 648 | } | ||
| 649 | |||
| 650 | arg = allocate_arg(); | ||
| 651 | arg->type = FILTER_ARG_EXP; | ||
| 652 | arg->op.type = etype; | ||
| 653 | arg->op.left = larg; | ||
| 654 | arg->op.right = rarg; | ||
| 655 | |||
| 656 | cont: | ||
| 657 | /* still need a cmp */ | ||
| 658 | return process_op(event, arg, parg, tok, error_str); | ||
| 659 | } | ||
| 660 | |||
| 661 | /* | ||
| 662 | * Input: tok | ||
| 663 | * Output: parg, tok | ||
| 664 | */ | ||
| 665 | static enum event_type | ||
| 666 | process_op_token(struct event_format *event, struct filter_arg *larg, | ||
| 667 | struct filter_arg **parg, enum event_type type, char **tok, | ||
| 668 | char **error_str) | ||
| 669 | { | ||
| 670 | enum filter_cmp_type ctype; | ||
| 671 | enum filter_exp_type etype = FILTER_EXP_NONE; | ||
| 672 | char *token; | ||
| 673 | |||
| 674 | token = *tok; | ||
| 675 | *parg = NULL; | ||
| 676 | |||
| 677 | if (type != EVENT_OP) { | ||
| 678 | *parg = larg; | ||
| 679 | return type; | ||
| 680 | } | ||
| 681 | |||
| 682 | if (strcmp(token, "&&") == 0 || strcmp(token, "||") == 0) { | ||
| 683 | /* handle boolean cases */ | ||
| 684 | return process_bool(event, larg, parg, tok, error_str); | ||
| 685 | } | ||
| 686 | |||
| 687 | /* Check for value expressions */ | ||
| 688 | if (strcmp(token, "+") == 0) { | ||
| 689 | etype = FILTER_EXP_ADD; | ||
| 690 | } else if (strcmp(token, "-") == 0) { | ||
| 691 | etype = FILTER_EXP_SUB; | ||
| 692 | } else if (strcmp(token, "*") == 0) { | ||
| 693 | etype = FILTER_EXP_MUL; | ||
| 694 | } else if (strcmp(token, "/") == 0) { | ||
| 695 | etype = FILTER_EXP_DIV; | ||
| 696 | } else if (strcmp(token, "%") == 0) { | ||
| 697 | etype = FILTER_EXP_MOD; | ||
| 698 | } else if (strcmp(token, ">>") == 0) { | ||
| 699 | etype = FILTER_EXP_RSHIFT; | ||
| 700 | } else if (strcmp(token, "<<") == 0) { | ||
| 701 | etype = FILTER_EXP_LSHIFT; | ||
| 702 | } else if (strcmp(token, "&") == 0) { | ||
| 703 | etype = FILTER_EXP_AND; | ||
| 704 | } else if (strcmp(token, "|") == 0) { | ||
| 705 | etype = FILTER_EXP_OR; | ||
| 706 | } else if (strcmp(token, "^") == 0) { | ||
| 707 | etype = FILTER_EXP_XOR; | ||
| 708 | } else if (strcmp(token, "~") == 0) | ||
| 709 | etype = FILTER_EXP_NOT; | ||
| 710 | |||
| 711 | if (etype != FILTER_EXP_NONE) { | ||
| 478 | free_token(token); | 712 | free_token(token); |
| 479 | return EVENT_ERROR; | 713 | return process_exp(event, etype, larg, parg, tok, error_str); |
| 480 | } | 714 | } |
| 481 | 715 | ||
| 482 | if (strcmp(token, "==") == 0) { | 716 | if (strcmp(token, "==") == 0) { |
| 483 | etype = FILTER_CMP_EQ; | 717 | ctype = FILTER_CMP_EQ; |
| 484 | } else if (strcmp(token, "!=") == 0) { | 718 | } else if (strcmp(token, "!=") == 0) { |
| 485 | etype = FILTER_CMP_NE; | 719 | ctype = FILTER_CMP_NE; |
| 486 | } else if (strcmp(token, "<") == 0) { | 720 | } else if (strcmp(token, "<") == 0) { |
| 487 | etype = FILTER_CMP_LT; | 721 | ctype = FILTER_CMP_LT; |
| 488 | } else if (strcmp(token, ">") == 0) { | 722 | } else if (strcmp(token, ">") == 0) { |
| 489 | etype = FILTER_CMP_GT; | 723 | ctype = FILTER_CMP_GT; |
| 490 | } else if (strcmp(token, "<=") == 0) { | 724 | } else if (strcmp(token, "<=") == 0) { |
| 491 | etype = FILTER_CMP_LE; | 725 | ctype = FILTER_CMP_LE; |
| 492 | } else if (strcmp(token, ">=") == 0) { | 726 | } else if (strcmp(token, ">=") == 0) { |
| 493 | etype = FILTER_CMP_GE; | 727 | ctype = FILTER_CMP_GE; |
| 494 | } else if (strcmp(token, "=~") == 0) { | 728 | } else if (strcmp(token, "=~") == 0) { |
| 495 | etype = FILTER_CMP_REGEX; | 729 | ctype = FILTER_CMP_REGEX; |
| 496 | } else if (strcmp(token, "!~") == 0) { | 730 | } else if (strcmp(token, "!~") == 0) { |
| 497 | etype = FILTER_CMP_NOT_REGEX; | 731 | ctype = FILTER_CMP_NOT_REGEX; |
| 498 | } else { | 732 | } else { |
| 499 | show_error(error_str, | 733 | show_error(error_str, |
| 500 | "Unknown op '%s' after '%s'", | 734 | "Unknown op '%s'", token); |
| 501 | token, field_name); | ||
| 502 | free_token(field_name); | ||
| 503 | free_token(token); | 735 | free_token(token); |
| 504 | return EVENT_ERROR; | 736 | return EVENT_ERROR; |
| 505 | } | 737 | } |
| 506 | op = token; | ||
| 507 | 738 | ||
| 508 | type = read_token(&token); | 739 | free_token(token); |
| 509 | if (type != EVENT_ITEM && type != EVENT_SQUOTE && type != EVENT_DQUOTE) { | 740 | *tok = NULL; |
| 510 | show_error(error_str, | 741 | return process_cmp(event, ctype, larg, parg, tok, error_str); |
| 511 | "Expected an item after '%s %s' instead of %s", | 742 | } |
| 512 | field_name, op, token); | ||
| 513 | free_token(field_name); | ||
| 514 | free_token(op); | ||
| 515 | free_token(token); | ||
| 516 | return EVENT_ERROR; | ||
| 517 | } | ||
| 518 | free_token(op); | ||
| 519 | 743 | ||
| 520 | field = pevent_find_any_field(event, field_name); | 744 | static enum event_type |
| 521 | free_token(field_name); | 745 | process_op(struct event_format *event, struct filter_arg *larg, |
| 746 | struct filter_arg **parg, char **tok, char **error_str) | ||
| 747 | { | ||
| 748 | enum event_type type; | ||
| 522 | 749 | ||
| 523 | arg = allocate_arg(); | 750 | *tok = NULL; |
| 751 | type = read_token(tok); | ||
| 752 | type = process_op_token(event, larg, parg, type, tok, error_str); | ||
| 524 | 753 | ||
| 525 | if (field) { | 754 | return type; |
| 526 | ret = process_valid_field(arg, field, etype, type, token, error_str); | 755 | } |
| 527 | if (ret < 0) { | ||
| 528 | free_arg(arg); | ||
| 529 | return EVENT_ERROR; | ||
| 530 | } | ||
| 531 | } else { | ||
| 532 | /* | ||
| 533 | * When an event does not contain a field in the | ||
| 534 | * filter, just make it false. | ||
| 535 | */ | ||
| 536 | arg->type = FILTER_ARG_BOOLEAN; | ||
| 537 | arg->bool.value = FILTER_FALSE; | ||
| 538 | } | ||
| 539 | 756 | ||
| 540 | free_token(token); | 757 | static enum event_type |
| 758 | process_filter(struct event_format *event, struct filter_arg **parg, | ||
| 759 | enum event_type type, char **tok, char **error_str) | ||
| 760 | { | ||
| 761 | struct filter_arg *larg = NULL; | ||
| 541 | 762 | ||
| 542 | type = read_token(tok); | 763 | *parg = NULL; |
| 543 | 764 | ||
| 544 | if (cont && type == EVENT_OP && | 765 | type = process_value_token(event, &larg, type, tok, error_str); |
| 545 | (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) { | 766 | free_token(*tok); |
| 546 | /* continue */; | 767 | *tok = NULL; |
| 547 | type = process_op(event, arg, parg, tok, error_str); | ||
| 548 | } else | ||
| 549 | *parg = arg; | ||
| 550 | 768 | ||
| 551 | return type; | 769 | return process_op(event, larg, parg, tok, error_str); |
| 552 | } | 770 | } |
| 553 | 771 | ||
| 554 | static enum event_type | 772 | static enum event_type |
| 555 | process_paren(struct event_format *event, struct filter_arg **parg, | 773 | process_paren(struct event_format *event, struct filter_arg **parg, |
| 556 | char **tok, char **error_str, int cont) | 774 | char **tok, char **error_str) |
| 557 | { | 775 | { |
| 558 | struct filter_arg *arg; | 776 | struct filter_arg *arg; |
| 559 | enum event_type type; | 777 | enum event_type type; |
| 560 | 778 | ||
| 561 | *parg = NULL; | 779 | *parg = NULL; |
| 562 | 780 | ||
| 563 | type = process_token(event, &arg, tok, error_str, 1); | 781 | type = process_token(event, &arg, tok, error_str, 0); |
| 564 | if (type == EVENT_ERROR) { | 782 | if (type == EVENT_ERROR) { |
| 565 | free_arg(arg); | 783 | free_arg(arg); |
| 566 | return type; | 784 | return type; |
| @@ -578,22 +796,16 @@ process_paren(struct event_format *event, struct filter_arg **parg, | |||
| 578 | return EVENT_ERROR; | 796 | return EVENT_ERROR; |
| 579 | } | 797 | } |
| 580 | free_token(*tok); | 798 | free_token(*tok); |
| 799 | *tok = NULL; | ||
| 581 | 800 | ||
| 582 | type = read_token(tok); | 801 | *parg = arg; |
| 583 | |||
| 584 | if (cont && type == EVENT_OP && | ||
| 585 | (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) { | ||
| 586 | /* continue */; | ||
| 587 | type = process_op(event, arg, parg, tok, error_str); | ||
| 588 | } else | ||
| 589 | *parg = arg; | ||
| 590 | 802 | ||
| 591 | return type; | 803 | return read_token(tok); |
| 592 | } | 804 | } |
| 593 | 805 | ||
| 594 | static enum event_type | 806 | static enum event_type |
| 595 | process_not(struct event_format *event, struct filter_arg **parg, | 807 | process_not(struct event_format *event, struct filter_arg **parg, |
| 596 | char **tok, char **error_str, int cont) | 808 | char **tok, char **error_str) |
| 597 | { | 809 | { |
| 598 | struct filter_arg *arg; | 810 | struct filter_arg *arg; |
| 599 | enum event_type type; | 811 | enum event_type type; |
| @@ -611,13 +823,14 @@ process_not(struct event_format *event, struct filter_arg **parg, | |||
| 611 | *tok = NULL; | 823 | *tok = NULL; |
| 612 | return EVENT_ERROR; | 824 | return EVENT_ERROR; |
| 613 | } | 825 | } |
| 826 | /* If the bool value is NULL, then make this into TRUE */ | ||
| 827 | if (!arg->op.right) { | ||
| 828 | arg->type = FILTER_ARG_BOOLEAN; | ||
| 829 | arg->bool.value = FILTER_TRUE; | ||
| 830 | } | ||
| 614 | 831 | ||
| 615 | if (cont && type == EVENT_OP && | 832 | free_token(*tok); |
| 616 | (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) { | 833 | *tok = NULL; |
| 617 | /* continue */; | ||
| 618 | type = process_op(event, arg, parg, tok, error_str); | ||
| 619 | } else | ||
| 620 | *parg = arg; | ||
| 621 | 834 | ||
| 622 | return type; | 835 | return type; |
| 623 | } | 836 | } |
| @@ -645,6 +858,14 @@ process_event(struct event_format *event, const char *filter_str, | |||
| 645 | *parg = NULL; | 858 | *parg = NULL; |
| 646 | return -1; | 859 | return -1; |
| 647 | } | 860 | } |
| 861 | |||
| 862 | /* If parg is NULL, then make it into FALSE */ | ||
| 863 | if (!*parg) { | ||
| 864 | *parg = allocate_arg(); | ||
| 865 | (*parg)->type = FILTER_ARG_BOOLEAN; | ||
| 866 | (*parg)->bool.value = FILTER_FALSE; | ||
| 867 | } | ||
| 868 | |||
| 648 | return 0; | 869 | return 0; |
| 649 | } | 870 | } |
| 650 | 871 | ||
| @@ -931,7 +1152,6 @@ int pevent_filter_copy(struct event_filter *dest, struct event_filter *source) | |||
| 931 | * Returns 0 on success and -1 if there was a problem updating, but | 1152 | * Returns 0 on success and -1 if there was a problem updating, but |
| 932 | * events may have still been updated on error. | 1153 | * events may have still been updated on error. |
| 933 | */ | 1154 | */ |
| 934 | |||
| 935 | int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, | 1155 | int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, |
| 936 | enum filter_trivial_type type) | 1156 | enum filter_trivial_type type) |
| 937 | { | 1157 | { |
| @@ -1099,31 +1319,102 @@ get_value(struct format_field *field, struct record *record) | |||
| 1099 | return val; | 1319 | return val; |
| 1100 | } | 1320 | } |
| 1101 | 1321 | ||
| 1322 | static unsigned long long | ||
| 1323 | get_arg_value(struct event_format *event, struct filter_arg *arg, struct record *record); | ||
| 1324 | |||
| 1325 | static unsigned long long | ||
| 1326 | get_exp_value(struct event_format *event, struct filter_arg *arg, struct record *record) | ||
| 1327 | { | ||
| 1328 | unsigned long long lval, rval; | ||
| 1329 | |||
| 1330 | lval = get_arg_value(event, arg->op.left, record); | ||
| 1331 | rval = get_arg_value(event, arg->op.right, record); | ||
| 1332 | |||
| 1333 | switch (arg->op.type) { | ||
| 1334 | case FILTER_EXP_ADD: | ||
| 1335 | return lval + rval; | ||
| 1336 | |||
| 1337 | case FILTER_EXP_SUB: | ||
| 1338 | return lval - rval; | ||
| 1339 | |||
| 1340 | case FILTER_EXP_MUL: | ||
| 1341 | return lval * rval; | ||
| 1342 | |||
| 1343 | case FILTER_EXP_DIV: | ||
| 1344 | return lval / rval; | ||
| 1345 | |||
| 1346 | case FILTER_EXP_MOD: | ||
| 1347 | return lval % rval; | ||
| 1348 | |||
| 1349 | case FILTER_EXP_RSHIFT: | ||
| 1350 | return lval >> rval; | ||
| 1351 | |||
| 1352 | case FILTER_EXP_LSHIFT: | ||
| 1353 | return lval << rval; | ||
| 1354 | |||
| 1355 | case FILTER_EXP_AND: | ||
| 1356 | return lval & rval; | ||
| 1357 | |||
| 1358 | case FILTER_EXP_OR: | ||
| 1359 | return lval | rval; | ||
| 1360 | |||
| 1361 | case FILTER_EXP_XOR: | ||
| 1362 | return lval ^ rval; | ||
| 1363 | |||
| 1364 | case FILTER_EXP_NOT: | ||
| 1365 | default: | ||
| 1366 | die("error in exp"); | ||
| 1367 | } | ||
| 1368 | return 0; | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | static unsigned long long | ||
| 1372 | get_arg_value(struct event_format *event, struct filter_arg *arg, struct record *record) | ||
| 1373 | { | ||
| 1374 | switch (arg->type) { | ||
| 1375 | case FILTER_ARG_FIELD: | ||
| 1376 | return get_value(arg->field.field, record); | ||
| 1377 | |||
| 1378 | case FILTER_ARG_VALUE: | ||
| 1379 | if (arg->value.type != FILTER_NUMBER) | ||
| 1380 | die("must have number field!"); | ||
| 1381 | return arg->value.val; | ||
| 1382 | |||
| 1383 | case FILTER_ARG_EXP: | ||
| 1384 | return get_exp_value(event, arg, record); | ||
| 1385 | |||
| 1386 | default: | ||
| 1387 | die("oops in filter"); | ||
| 1388 | } | ||
| 1389 | return 0; | ||
| 1390 | } | ||
| 1391 | |||
| 1102 | static int test_num(struct event_format *event, | 1392 | static int test_num(struct event_format *event, |
| 1103 | struct filter_arg *arg, struct record *record) | 1393 | struct filter_arg *arg, struct record *record) |
| 1104 | { | 1394 | { |
| 1105 | unsigned long long val; | 1395 | unsigned long long lval, rval; |
| 1106 | 1396 | ||
| 1107 | val = get_value(arg->num.field, record); | 1397 | lval = get_arg_value(event, arg->num.left, record); |
| 1398 | rval = get_arg_value(event, arg->num.right, record); | ||
| 1108 | 1399 | ||
| 1109 | switch (arg->num.type) { | 1400 | switch (arg->num.type) { |
| 1110 | case FILTER_CMP_EQ: | 1401 | case FILTER_CMP_EQ: |
| 1111 | return val == arg->num.val; | 1402 | return lval == rval; |
| 1112 | 1403 | ||
| 1113 | case FILTER_CMP_NE: | 1404 | case FILTER_CMP_NE: |
| 1114 | return val != arg->num.val; | 1405 | return lval != rval; |
| 1115 | 1406 | ||
| 1116 | case FILTER_CMP_GT: | 1407 | case FILTER_CMP_GT: |
| 1117 | return val > arg->num.val; | 1408 | return lval > rval; |
| 1118 | 1409 | ||
| 1119 | case FILTER_CMP_LT: | 1410 | case FILTER_CMP_LT: |
| 1120 | return val < arg->num.val; | 1411 | return lval < rval; |
| 1121 | 1412 | ||
| 1122 | case FILTER_CMP_GE: | 1413 | case FILTER_CMP_GE: |
| 1123 | return val >= arg->num.val; | 1414 | return lval >= rval; |
| 1124 | 1415 | ||
| 1125 | case FILTER_CMP_LE: | 1416 | case FILTER_CMP_LE: |
| 1126 | return val <= arg->num.val; | 1417 | return lval <= rval; |
| 1127 | 1418 | ||
| 1128 | default: | 1419 | default: |
| 1129 | /* ?? */ | 1420 | /* ?? */ |
| @@ -1208,7 +1499,17 @@ static int test_filter(struct event_format *event, | |||
| 1208 | case FILTER_ARG_STR: | 1499 | case FILTER_ARG_STR: |
| 1209 | return test_str(event, arg, record); | 1500 | return test_str(event, arg, record); |
| 1210 | 1501 | ||
| 1502 | case FILTER_ARG_EXP: | ||
| 1503 | case FILTER_ARG_VALUE: | ||
| 1504 | case FILTER_ARG_FIELD: | ||
| 1505 | /* | ||
| 1506 | * Expressions, fields and values evaluate | ||
| 1507 | * to true if they return non zero | ||
| 1508 | */ | ||
| 1509 | return !!get_arg_value(event, arg, record); | ||
| 1510 | |||
| 1211 | default: | 1511 | default: |
| 1512 | die("oops!"); | ||
| 1212 | /* ?? */ | 1513 | /* ?? */ |
| 1213 | return 0; | 1514 | return 0; |
| 1214 | } | 1515 | } |
| @@ -1385,12 +1686,88 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) | |||
| 1385 | return str; | 1686 | return str; |
| 1386 | } | 1687 | } |
| 1387 | 1688 | ||
| 1689 | static char *val_to_str(struct event_filter *filter, struct filter_arg *arg) | ||
| 1690 | { | ||
| 1691 | char *str; | ||
| 1692 | |||
| 1693 | str = malloc_or_die(30); | ||
| 1694 | |||
| 1695 | snprintf(str, 30, "%lld", arg->value.val); | ||
| 1696 | |||
| 1697 | return str; | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | static char *field_to_str(struct event_filter *filter, struct filter_arg *arg) | ||
| 1701 | { | ||
| 1702 | return strdup(arg->field.field->name); | ||
| 1703 | } | ||
| 1704 | |||
| 1705 | static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) | ||
| 1706 | { | ||
| 1707 | char *lstr; | ||
| 1708 | char *rstr; | ||
| 1709 | char *op; | ||
| 1710 | char *str; | ||
| 1711 | int len; | ||
| 1712 | |||
| 1713 | lstr = arg_to_str(filter, arg->op.left); | ||
| 1714 | rstr = arg_to_str(filter, arg->op.right); | ||
| 1715 | |||
| 1716 | switch (arg->op.type) { | ||
| 1717 | case FILTER_EXP_ADD: | ||
| 1718 | op = "+"; | ||
| 1719 | break; | ||
| 1720 | case FILTER_EXP_SUB: | ||
| 1721 | op = "-"; | ||
| 1722 | break; | ||
| 1723 | case FILTER_EXP_MUL: | ||
| 1724 | op = "*"; | ||
| 1725 | break; | ||
| 1726 | case FILTER_EXP_DIV: | ||
| 1727 | op = "/"; | ||
| 1728 | break; | ||
| 1729 | case FILTER_EXP_MOD: | ||
| 1730 | op = "%"; | ||
| 1731 | break; | ||
| 1732 | case FILTER_EXP_RSHIFT: | ||
| 1733 | op = ">>"; | ||
| 1734 | break; | ||
| 1735 | case FILTER_EXP_LSHIFT: | ||
| 1736 | op = "<<"; | ||
| 1737 | break; | ||
| 1738 | case FILTER_EXP_AND: | ||
| 1739 | op = "&"; | ||
| 1740 | break; | ||
| 1741 | case FILTER_EXP_OR: | ||
| 1742 | op = "|"; | ||
| 1743 | break; | ||
| 1744 | case FILTER_EXP_XOR: | ||
| 1745 | op = "^"; | ||
| 1746 | break; | ||
| 1747 | default: | ||
| 1748 | die("oops in exp"); | ||
| 1749 | } | ||
| 1750 | |||
| 1751 | len = strlen(op) + strlen(lstr) + strlen(rstr) + 4; | ||
| 1752 | str = malloc_or_die(len); | ||
| 1753 | snprintf(str, len, "%s %s %s", lstr, op, rstr); | ||
| 1754 | free(lstr); | ||
| 1755 | free(rstr); | ||
| 1756 | |||
| 1757 | return str; | ||
| 1758 | } | ||
| 1759 | |||
| 1388 | static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) | 1760 | static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) |
| 1389 | { | 1761 | { |
| 1762 | char *lstr; | ||
| 1763 | char *rstr; | ||
| 1390 | char *str = NULL; | 1764 | char *str = NULL; |
| 1391 | char *op = NULL; | 1765 | char *op = NULL; |
| 1392 | int len; | 1766 | int len; |
| 1393 | 1767 | ||
| 1768 | lstr = arg_to_str(filter, arg->num.left); | ||
| 1769 | rstr = arg_to_str(filter, arg->num.right); | ||
| 1770 | |||
| 1394 | switch (arg->num.type) { | 1771 | switch (arg->num.type) { |
| 1395 | case FILTER_CMP_EQ: | 1772 | case FILTER_CMP_EQ: |
| 1396 | op = "=="; | 1773 | op = "=="; |
| @@ -1415,22 +1792,19 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) | |||
| 1415 | if (!op) | 1792 | if (!op) |
| 1416 | op = "<="; | 1793 | op = "<="; |
| 1417 | 1794 | ||
| 1418 | len = strlen(arg->num.field->name) + strlen(op) + 30; | 1795 | len = strlen(lstr) + strlen(op) + strlen(rstr) + 4; |
| 1419 | str = malloc_or_die(len); | 1796 | str = malloc_or_die(len); |
| 1420 | if (arg->num.field->flags & FIELD_IS_SIGNED) | 1797 | sprintf(str, "%s %s %s", lstr, op, rstr); |
| 1421 | snprintf(str, len, "%s %s %lld", | 1798 | |
| 1422 | arg->num.field->name, | ||
| 1423 | op, arg->num.val); | ||
| 1424 | else | ||
| 1425 | snprintf(str, len, "%s %s %llu", | ||
| 1426 | arg->num.field->name, | ||
| 1427 | op, arg->num.val); | ||
| 1428 | break; | 1799 | break; |
| 1429 | 1800 | ||
| 1430 | default: | 1801 | default: |
| 1431 | /* ?? */ | 1802 | /* ?? */ |
| 1432 | break; | 1803 | break; |
| 1433 | } | 1804 | } |
| 1805 | |||
| 1806 | free(lstr); | ||
| 1807 | free(rstr); | ||
| 1434 | return str; | 1808 | return str; |
| 1435 | } | 1809 | } |
| 1436 | 1810 | ||
| @@ -1493,6 +1867,15 @@ static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) | |||
| 1493 | case FILTER_ARG_STR: | 1867 | case FILTER_ARG_STR: |
| 1494 | return str_to_str(filter, arg); | 1868 | return str_to_str(filter, arg); |
| 1495 | 1869 | ||
| 1870 | case FILTER_ARG_VALUE: | ||
| 1871 | return val_to_str(filter, arg); | ||
| 1872 | |||
| 1873 | case FILTER_ARG_FIELD: | ||
| 1874 | return field_to_str(filter, arg); | ||
| 1875 | |||
| 1876 | case FILTER_ARG_EXP: | ||
| 1877 | return exp_to_str(filter, arg); | ||
| 1878 | |||
| 1496 | default: | 1879 | default: |
| 1497 | /* ?? */ | 1880 | /* ?? */ |
| 1498 | return NULL; | 1881 | return NULL; |
