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; |