diff options
Diffstat (limited to 'tools/lib/traceevent/parse-filter.c')
-rw-r--r-- | tools/lib/traceevent/parse-filter.c | 2283 |
1 files changed, 0 insertions, 2283 deletions
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c deleted file mode 100644 index c271aeeb227d..000000000000 --- a/tools/lib/traceevent/parse-filter.c +++ /dev/null @@ -1,2283 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> - * - */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <errno.h> -#include <sys/types.h> - -#include "event-parse.h" -#include "event-parse-local.h" -#include "event-utils.h" - -#define COMM "COMM" -#define CPU "CPU" - -static struct tep_format_field comm = { - .name = "COMM", -}; - -static struct tep_format_field cpu = { - .name = "CPU", -}; - -struct event_list { - struct event_list *next; - struct tep_event *event; -}; - -static void show_error(char *error_buf, const char *fmt, ...) -{ - unsigned long long index; - const char *input; - va_list ap; - int len; - int i; - - input = tep_get_input_buf(); - index = tep_get_input_buf_ptr(); - len = input ? strlen(input) : 0; - - if (len) { - strcpy(error_buf, input); - error_buf[len] = '\n'; - for (i = 1; i < len && i < index; i++) - error_buf[len+i] = ' '; - error_buf[len + i] = '^'; - error_buf[len + i + 1] = '\n'; - len += i+2; - } - - va_start(ap, fmt); - vsnprintf(error_buf + len, TEP_FILTER_ERROR_BUFSZ - len, fmt, ap); - va_end(ap); -} - -static void free_token(char *token) -{ - tep_free_token(token); -} - -static enum tep_event_type read_token(char **tok) -{ - enum tep_event_type type; - char *token = NULL; - - do { - free_token(token); - type = tep_read_token(&token); - } while (type == TEP_EVENT_NEWLINE || type == TEP_EVENT_SPACE); - - /* If token is = or ! check to see if the next char is ~ */ - if (token && - (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && - tep_peek_char() == '~') { - /* append it */ - *tok = malloc(3); - if (*tok == NULL) { - free_token(token); - return TEP_EVENT_ERROR; - } - sprintf(*tok, "%c%c", *token, '~'); - free_token(token); - /* Now remove the '~' from the buffer */ - tep_read_token(&token); - free_token(token); - } else - *tok = token; - - return type; -} - -static int filter_cmp(const void *a, const void *b) -{ - const struct tep_filter_type *ea = a; - const struct tep_filter_type *eb = b; - - if (ea->event_id < eb->event_id) - return -1; - - if (ea->event_id > eb->event_id) - return 1; - - return 0; -} - -static struct tep_filter_type * -find_filter_type(struct tep_event_filter *filter, int id) -{ - struct tep_filter_type *filter_type; - struct tep_filter_type key; - - key.event_id = id; - - filter_type = bsearch(&key, filter->event_filters, - filter->filters, - sizeof(*filter->event_filters), - filter_cmp); - - return filter_type; -} - -static struct tep_filter_type * -add_filter_type(struct tep_event_filter *filter, int id) -{ - struct tep_filter_type *filter_type; - int i; - - filter_type = find_filter_type(filter, id); - if (filter_type) - return filter_type; - - filter_type = realloc(filter->event_filters, - sizeof(*filter->event_filters) * - (filter->filters + 1)); - if (!filter_type) - return NULL; - - filter->event_filters = filter_type; - - for (i = 0; i < filter->filters; i++) { - if (filter->event_filters[i].event_id > id) - break; - } - - if (i < filter->filters) - memmove(&filter->event_filters[i+1], - &filter->event_filters[i], - sizeof(*filter->event_filters) * - (filter->filters - i)); - - filter_type = &filter->event_filters[i]; - filter_type->event_id = id; - filter_type->event = tep_find_event(filter->tep, id); - filter_type->filter = NULL; - - filter->filters++; - - return filter_type; -} - -/** - * tep_filter_alloc - create a new event filter - * @tep: The tep that this filter is associated with - */ -struct tep_event_filter *tep_filter_alloc(struct tep_handle *tep) -{ - struct tep_event_filter *filter; - - filter = malloc(sizeof(*filter)); - if (filter == NULL) - return NULL; - - memset(filter, 0, sizeof(*filter)); - filter->tep = tep; - tep_ref(tep); - - return filter; -} - -static struct tep_filter_arg *allocate_arg(void) -{ - return calloc(1, sizeof(struct tep_filter_arg)); -} - -static void free_arg(struct tep_filter_arg *arg) -{ - if (!arg) - return; - - switch (arg->type) { - case TEP_FILTER_ARG_NONE: - case TEP_FILTER_ARG_BOOLEAN: - break; - - case TEP_FILTER_ARG_NUM: - free_arg(arg->num.left); - free_arg(arg->num.right); - break; - - case TEP_FILTER_ARG_EXP: - free_arg(arg->exp.left); - free_arg(arg->exp.right); - break; - - case TEP_FILTER_ARG_STR: - free(arg->str.val); - regfree(&arg->str.reg); - free(arg->str.buffer); - break; - - case TEP_FILTER_ARG_VALUE: - if (arg->value.type == TEP_FILTER_STRING || - arg->value.type == TEP_FILTER_CHAR) - free(arg->value.str); - break; - - case TEP_FILTER_ARG_OP: - free_arg(arg->op.left); - free_arg(arg->op.right); - default: - break; - } - - free(arg); -} - -static int add_event(struct event_list **events, - struct tep_event *event) -{ - struct event_list *list; - - list = malloc(sizeof(*list)); - if (list == NULL) - return -1; - - list->next = *events; - *events = list; - list->event = event; - return 0; -} - -static int event_match(struct tep_event *event, - regex_t *sreg, regex_t *ereg) -{ - if (sreg) { - return !regexec(sreg, event->system, 0, NULL, 0) && - !regexec(ereg, event->name, 0, NULL, 0); - } - - return !regexec(ereg, event->system, 0, NULL, 0) || - !regexec(ereg, event->name, 0, NULL, 0); -} - -static enum tep_errno -find_event(struct tep_handle *tep, struct event_list **events, - char *sys_name, char *event_name) -{ - struct tep_event *event; - regex_t ereg; - regex_t sreg; - int match = 0; - int fail = 0; - char *reg; - int ret; - int i; - - if (!event_name) { - /* if no name is given, then swap sys and name */ - event_name = sys_name; - sys_name = NULL; - } - - ret = asprintf(®, "^%s$", event_name); - if (ret < 0) - return TEP_ERRNO__MEM_ALLOC_FAILED; - - ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); - free(reg); - - if (ret) - return TEP_ERRNO__INVALID_EVENT_NAME; - - if (sys_name) { - ret = asprintf(®, "^%s$", sys_name); - if (ret < 0) { - regfree(&ereg); - return TEP_ERRNO__MEM_ALLOC_FAILED; - } - - ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); - free(reg); - if (ret) { - regfree(&ereg); - return TEP_ERRNO__INVALID_EVENT_NAME; - } - } - - for (i = 0; i < tep->nr_events; i++) { - event = tep->events[i]; - if (event_match(event, sys_name ? &sreg : NULL, &ereg)) { - match = 1; - if (add_event(events, event) < 0) { - fail = 1; - break; - } - } - } - - regfree(&ereg); - if (sys_name) - regfree(&sreg); - - if (!match) - return TEP_ERRNO__EVENT_NOT_FOUND; - if (fail) - return TEP_ERRNO__MEM_ALLOC_FAILED; - - return 0; -} - -static void free_events(struct event_list *events) -{ - struct event_list *event; - - while (events) { - event = events; - events = events->next; - free(event); - } -} - -static enum tep_errno -create_arg_item(struct tep_event *event, const char *token, - enum tep_event_type type, struct tep_filter_arg **parg, char *error_str) -{ - struct tep_format_field *field; - struct tep_filter_arg *arg; - - arg = allocate_arg(); - if (arg == NULL) { - show_error(error_str, "failed to allocate filter arg"); - return TEP_ERRNO__MEM_ALLOC_FAILED; - } - - switch (type) { - - case TEP_EVENT_SQUOTE: - case TEP_EVENT_DQUOTE: - arg->type = TEP_FILTER_ARG_VALUE; - arg->value.type = - type == TEP_EVENT_DQUOTE ? TEP_FILTER_STRING : TEP_FILTER_CHAR; - arg->value.str = strdup(token); - if (!arg->value.str) { - free_arg(arg); - show_error(error_str, "failed to allocate string filter arg"); - return TEP_ERRNO__MEM_ALLOC_FAILED; - } - break; - case TEP_EVENT_ITEM: - /* if it is a number, then convert it */ - if (isdigit(token[0])) { - arg->type = TEP_FILTER_ARG_VALUE; - arg->value.type = TEP_FILTER_NUMBER; - arg->value.val = strtoull(token, NULL, 0); - break; - } - /* Consider this a field */ - field = tep_find_any_field(event, token); - if (!field) { - /* If token is 'COMM' or 'CPU' then it is special */ - if (strcmp(token, COMM) == 0) { - field = &comm; - } else if (strcmp(token, CPU) == 0) { - field = &cpu; - } else { - /* not a field, Make it false */ - arg->type = TEP_FILTER_ARG_BOOLEAN; - arg->boolean.value = TEP_FILTER_FALSE; - break; - } - } - arg->type = TEP_FILTER_ARG_FIELD; - arg->field.field = field; - break; - default: - free_arg(arg); - show_error(error_str, "expected a value but found %s", token); - return TEP_ERRNO__UNEXPECTED_TYPE; - } - *parg = arg; - return 0; -} - -static struct tep_filter_arg * -create_arg_op(enum tep_filter_op_type btype) -{ - struct tep_filter_arg *arg; - - arg = allocate_arg(); - if (!arg) - return NULL; - - arg->type = TEP_FILTER_ARG_OP; - arg->op.type = btype; - - return arg; -} - -static struct tep_filter_arg * -create_arg_exp(enum tep_filter_exp_type etype) -{ - struct tep_filter_arg *arg; - - arg = allocate_arg(); - if (!arg) - return NULL; - - arg->type = TEP_FILTER_ARG_EXP; - arg->exp.type = etype; - - return arg; -} - -static struct tep_filter_arg * -create_arg_cmp(enum tep_filter_cmp_type ctype) -{ - struct tep_filter_arg *arg; - - arg = allocate_arg(); - if (!arg) - return NULL; - - /* Use NUM and change if necessary */ - arg->type = TEP_FILTER_ARG_NUM; - arg->num.type = ctype; - - return arg; -} - -static enum tep_errno -add_right(struct tep_filter_arg *op, struct tep_filter_arg *arg, char *error_str) -{ - struct tep_filter_arg *left; - char *str; - int op_type; - int ret; - - switch (op->type) { - case TEP_FILTER_ARG_EXP: - if (op->exp.right) - goto out_fail; - op->exp.right = arg; - break; - - case TEP_FILTER_ARG_OP: - if (op->op.right) - goto out_fail; - op->op.right = arg; - break; - - case TEP_FILTER_ARG_NUM: - if (op->op.right) - goto out_fail; - /* - * The arg must be num, str, or field - */ - switch (arg->type) { - case TEP_FILTER_ARG_VALUE: - case TEP_FILTER_ARG_FIELD: - break; - default: - show_error(error_str, "Illegal rvalue"); - return TEP_ERRNO__ILLEGAL_RVALUE; - } - - /* - * Depending on the type, we may need to - * convert this to a string or regex. - */ - switch (arg->value.type) { - case TEP_FILTER_CHAR: - /* - * A char should be converted to number if - * the string is 1 byte, and the compare - * is not a REGEX. - */ - if (strlen(arg->value.str) == 1 && - op->num.type != TEP_FILTER_CMP_REGEX && - op->num.type != TEP_FILTER_CMP_NOT_REGEX) { - arg->value.type = TEP_FILTER_NUMBER; - goto do_int; - } - /* fall through */ - case TEP_FILTER_STRING: - - /* convert op to a string arg */ - op_type = op->num.type; - left = op->num.left; - str = arg->value.str; - - /* reset the op for the new field */ - memset(op, 0, sizeof(*op)); - - /* - * If left arg was a field not found then - * NULL the entire op. - */ - if (left->type == TEP_FILTER_ARG_BOOLEAN) { - free_arg(left); - free_arg(arg); - op->type = TEP_FILTER_ARG_BOOLEAN; - op->boolean.value = TEP_FILTER_FALSE; - break; - } - - /* Left arg must be a field */ - if (left->type != TEP_FILTER_ARG_FIELD) { - show_error(error_str, - "Illegal lvalue for string comparison"); - return TEP_ERRNO__ILLEGAL_LVALUE; - } - - /* Make sure this is a valid string compare */ - switch (op_type) { - case TEP_FILTER_CMP_EQ: - op_type = TEP_FILTER_CMP_MATCH; - break; - case TEP_FILTER_CMP_NE: - op_type = TEP_FILTER_CMP_NOT_MATCH; - break; - - case TEP_FILTER_CMP_REGEX: - case TEP_FILTER_CMP_NOT_REGEX: - ret = regcomp(&op->str.reg, str, REG_ICASE|REG_NOSUB); - if (ret) { - show_error(error_str, - "RegEx '%s' did not compute", - str); - return TEP_ERRNO__INVALID_REGEX; - } - break; - default: - show_error(error_str, - "Illegal comparison for string"); - return TEP_ERRNO__ILLEGAL_STRING_CMP; - } - - op->type = TEP_FILTER_ARG_STR; - op->str.type = op_type; - op->str.field = left->field.field; - op->str.val = strdup(str); - if (!op->str.val) { - show_error(error_str, "Failed to allocate string filter"); - return TEP_ERRNO__MEM_ALLOC_FAILED; - } - /* - * Need a buffer to copy data for tests - */ - op->str.buffer = malloc(op->str.field->size + 1); - if (!op->str.buffer) { - show_error(error_str, "Failed to allocate string filter"); - return TEP_ERRNO__MEM_ALLOC_FAILED; - } - /* Null terminate this buffer */ - op->str.buffer[op->str.field->size] = 0; - - /* We no longer have left or right args */ - free_arg(arg); - free_arg(left); - - break; - - case TEP_FILTER_NUMBER: - - do_int: - switch (op->num.type) { - case TEP_FILTER_CMP_REGEX: - case TEP_FILTER_CMP_NOT_REGEX: - show_error(error_str, - "Op not allowed with integers"); - return TEP_ERRNO__ILLEGAL_INTEGER_CMP; - - default: - break; - } - - /* numeric compare */ - op->num.right = arg; - break; - default: - goto out_fail; - } - break; - default: - goto out_fail; - } - - return 0; - - out_fail: - show_error(error_str, "Syntax error"); - return TEP_ERRNO__SYNTAX_ERROR; -} - -static struct tep_filter_arg * -rotate_op_right(struct tep_filter_arg *a, struct tep_filter_arg *b) -{ - struct tep_filter_arg *arg; - - arg = a->op.right; - a->op.right = b; - return arg; -} - -static enum tep_errno add_left(struct tep_filter_arg *op, struct tep_filter_arg *arg) -{ - switch (op->type) { - case TEP_FILTER_ARG_EXP: - if (arg->type == TEP_FILTER_ARG_OP) - arg = rotate_op_right(arg, op); - op->exp.left = arg; - break; - - case TEP_FILTER_ARG_OP: - op->op.left = arg; - break; - case TEP_FILTER_ARG_NUM: - if (arg->type == TEP_FILTER_ARG_OP) - arg = rotate_op_right(arg, op); - - /* left arg of compares must be a field */ - if (arg->type != TEP_FILTER_ARG_FIELD && - arg->type != TEP_FILTER_ARG_BOOLEAN) - return TEP_ERRNO__INVALID_ARG_TYPE; - op->num.left = arg; - break; - default: - return TEP_ERRNO__INVALID_ARG_TYPE; - } - return 0; -} - -enum op_type { - OP_NONE, - OP_BOOL, - OP_NOT, - OP_EXP, - OP_CMP, -}; - -static enum op_type process_op(const char *token, - enum tep_filter_op_type *btype, - enum tep_filter_cmp_type *ctype, - enum tep_filter_exp_type *etype) -{ - *btype = TEP_FILTER_OP_NOT; - *etype = TEP_FILTER_EXP_NONE; - *ctype = TEP_FILTER_CMP_NONE; - - if (strcmp(token, "&&") == 0) - *btype = TEP_FILTER_OP_AND; - else if (strcmp(token, "||") == 0) - *btype = TEP_FILTER_OP_OR; - else if (strcmp(token, "!") == 0) - return OP_NOT; - - if (*btype != TEP_FILTER_OP_NOT) - return OP_BOOL; - - /* Check for value expressions */ - if (strcmp(token, "+") == 0) { - *etype = TEP_FILTER_EXP_ADD; - } else if (strcmp(token, "-") == 0) { - *etype = TEP_FILTER_EXP_SUB; - } else if (strcmp(token, "*") == 0) { - *etype = TEP_FILTER_EXP_MUL; - } else if (strcmp(token, "/") == 0) { - *etype = TEP_FILTER_EXP_DIV; - } else if (strcmp(token, "%") == 0) { - *etype = TEP_FILTER_EXP_MOD; - } else if (strcmp(token, ">>") == 0) { - *etype = TEP_FILTER_EXP_RSHIFT; - } else if (strcmp(token, "<<") == 0) { - *etype = TEP_FILTER_EXP_LSHIFT; - } else if (strcmp(token, "&") == 0) { - *etype = TEP_FILTER_EXP_AND; - } else if (strcmp(token, "|") == 0) { - *etype = TEP_FILTER_EXP_OR; - } else if (strcmp(token, "^") == 0) { - *etype = TEP_FILTER_EXP_XOR; - } else if (strcmp(token, "~") == 0) - *etype = TEP_FILTER_EXP_NOT; - - if (*etype != TEP_FILTER_EXP_NONE) - return OP_EXP; - - /* Check for compares */ - if (strcmp(token, "==") == 0) - *ctype = TEP_FILTER_CMP_EQ; - else if (strcmp(token, "!=") == 0) - *ctype = TEP_FILTER_CMP_NE; - else if (strcmp(token, "<") == 0) - *ctype = TEP_FILTER_CMP_LT; - else if (strcmp(token, ">") == 0) - *ctype = TEP_FILTER_CMP_GT; - else if (strcmp(token, "<=") == 0) - *ctype = TEP_FILTER_CMP_LE; - else if (strcmp(token, ">=") == 0) - *ctype = TEP_FILTER_CMP_GE; - else if (strcmp(token, "=~") == 0) - *ctype = TEP_FILTER_CMP_REGEX; - else if (strcmp(token, "!~") == 0) - *ctype = TEP_FILTER_CMP_NOT_REGEX; - else - return OP_NONE; - - return OP_CMP; -} - -static int check_op_done(struct tep_filter_arg *arg) -{ - switch (arg->type) { - case TEP_FILTER_ARG_EXP: - return arg->exp.right != NULL; - - case TEP_FILTER_ARG_OP: - return arg->op.right != NULL; - - case TEP_FILTER_ARG_NUM: - return arg->num.right != NULL; - - case TEP_FILTER_ARG_STR: - /* A string conversion is always done */ - return 1; - - case TEP_FILTER_ARG_BOOLEAN: - /* field not found, is ok */ - return 1; - - default: - return 0; - } -} - -enum filter_vals { - FILTER_VAL_NORM, - FILTER_VAL_FALSE, - FILTER_VAL_TRUE, -}; - -static enum tep_errno -reparent_op_arg(struct tep_filter_arg *parent, struct tep_filter_arg *old_child, - struct tep_filter_arg *arg, char *error_str) -{ - struct tep_filter_arg *other_child; - struct tep_filter_arg **ptr; - - if (parent->type != TEP_FILTER_ARG_OP && - arg->type != TEP_FILTER_ARG_OP) { - show_error(error_str, "can not reparent other than OP"); - return TEP_ERRNO__REPARENT_NOT_OP; - } - - /* Get the sibling */ - if (old_child->op.right == arg) { - ptr = &old_child->op.right; - other_child = old_child->op.left; - } else if (old_child->op.left == arg) { - ptr = &old_child->op.left; - other_child = old_child->op.right; - } else { - show_error(error_str, "Error in reparent op, find other child"); - return TEP_ERRNO__REPARENT_FAILED; - } - - /* Detach arg from old_child */ - *ptr = NULL; - - /* Check for root */ - if (parent == old_child) { - free_arg(other_child); - *parent = *arg; - /* Free arg without recussion */ - free(arg); - return 0; - } - - if (parent->op.right == old_child) - ptr = &parent->op.right; - else if (parent->op.left == old_child) - ptr = &parent->op.left; - else { - show_error(error_str, "Error in reparent op"); - return TEP_ERRNO__REPARENT_FAILED; - } - - *ptr = arg; - - free_arg(old_child); - return 0; -} - -/* Returns either filter_vals (success) or tep_errno (failfure) */ -static int test_arg(struct tep_filter_arg *parent, struct tep_filter_arg *arg, - char *error_str) -{ - int lval, rval; - - switch (arg->type) { - - /* bad case */ - case TEP_FILTER_ARG_BOOLEAN: - return FILTER_VAL_FALSE + arg->boolean.value; - - /* good cases: */ - case TEP_FILTER_ARG_STR: - case TEP_FILTER_ARG_VALUE: - case TEP_FILTER_ARG_FIELD: - return FILTER_VAL_NORM; - - case TEP_FILTER_ARG_EXP: - lval = test_arg(arg, arg->exp.left, error_str); - if (lval != FILTER_VAL_NORM) - return lval; - rval = test_arg(arg, arg->exp.right, error_str); - if (rval != FILTER_VAL_NORM) - return rval; - return FILTER_VAL_NORM; - - case TEP_FILTER_ARG_NUM: - lval = test_arg(arg, arg->num.left, error_str); - if (lval != FILTER_VAL_NORM) - return lval; - rval = test_arg(arg, arg->num.right, error_str); - if (rval != FILTER_VAL_NORM) - return rval; - return FILTER_VAL_NORM; - - case TEP_FILTER_ARG_OP: - if (arg->op.type != TEP_FILTER_OP_NOT) { - lval = test_arg(arg, arg->op.left, error_str); - switch (lval) { - case FILTER_VAL_NORM: - break; - case FILTER_VAL_TRUE: - if (arg->op.type == TEP_FILTER_OP_OR) - return FILTER_VAL_TRUE; - rval = test_arg(arg, arg->op.right, error_str); - if (rval != FILTER_VAL_NORM) - return rval; - - return reparent_op_arg(parent, arg, arg->op.right, - error_str); - - case FILTER_VAL_FALSE: - if (arg->op.type == TEP_FILTER_OP_AND) - return FILTER_VAL_FALSE; - rval = test_arg(arg, arg->op.right, error_str); - if (rval != FILTER_VAL_NORM) - return rval; - - return reparent_op_arg(parent, arg, arg->op.right, - error_str); - - default: - return lval; - } - } - - rval = test_arg(arg, arg->op.right, error_str); - switch (rval) { - case FILTER_VAL_NORM: - default: - break; - - case FILTER_VAL_TRUE: - if (arg->op.type == TEP_FILTER_OP_OR) - return FILTER_VAL_TRUE; - if (arg->op.type == TEP_FILTER_OP_NOT) - return FILTER_VAL_FALSE; - - return reparent_op_arg(parent, arg, arg->op.left, - error_str); - - case FILTER_VAL_FALSE: - if (arg->op.type == TEP_FILTER_OP_AND) - return FILTER_VAL_FALSE; - if (arg->op.type == TEP_FILTER_OP_NOT) - return FILTER_VAL_TRUE; - - return reparent_op_arg(parent, arg, arg->op.left, - error_str); - } - - return rval; - default: - show_error(error_str, "bad arg in filter tree"); - return TEP_ERRNO__BAD_FILTER_ARG; - } - return FILTER_VAL_NORM; -} - -/* Remove any unknown event fields */ -static int collapse_tree(struct tep_filter_arg *arg, - struct tep_filter_arg **arg_collapsed, char *error_str) -{ - int ret; - - ret = test_arg(arg, arg, error_str); - switch (ret) { - case FILTER_VAL_NORM: - break; - - case FILTER_VAL_TRUE: - case FILTER_VAL_FALSE: - free_arg(arg); - arg = allocate_arg(); - if (arg) { - arg->type = TEP_FILTER_ARG_BOOLEAN; - arg->boolean.value = ret == FILTER_VAL_TRUE; - } else { - show_error(error_str, "Failed to allocate filter arg"); - ret = TEP_ERRNO__MEM_ALLOC_FAILED; - } - break; - - default: - /* test_arg() already set the error_str */ - free_arg(arg); - arg = NULL; - break; - } - - *arg_collapsed = arg; - return ret; -} - -static enum tep_errno -process_filter(struct tep_event *event, struct tep_filter_arg **parg, - char *error_str, int not) -{ - enum tep_event_type type; - char *token = NULL; - struct tep_filter_arg *current_op = NULL; - struct tep_filter_arg *current_exp = NULL; - struct tep_filter_arg *left_item = NULL; - struct tep_filter_arg *arg = NULL; - enum op_type op_type; - enum tep_filter_op_type btype; - enum tep_filter_exp_type etype; - enum tep_filter_cmp_type ctype; - enum tep_errno ret; - - *parg = NULL; - - do { - free(token); - type = read_token(&token); - switch (type) { - case TEP_EVENT_SQUOTE: - case TEP_EVENT_DQUOTE: - case TEP_EVENT_ITEM: - ret = create_arg_item(event, token, type, &arg, error_str); - if (ret < 0) - goto fail; - if (!left_item) - left_item = arg; - else if (current_exp) { - ret = add_right(current_exp, arg, error_str); - if (ret < 0) - goto fail; - left_item = NULL; - /* Not's only one one expression */ - if (not) { - arg = NULL; - if (current_op) - goto fail_syntax; - free(token); - *parg = current_exp; - return 0; - } - } else - goto fail_syntax; - arg = NULL; - break; - - case TEP_EVENT_DELIM: - if (*token == ',') { - show_error(error_str, "Illegal token ','"); - ret = TEP_ERRNO__ILLEGAL_TOKEN; - goto fail; - } - - if (*token == '(') { - if (left_item) { - show_error(error_str, - "Open paren can not come after item"); - ret = TEP_ERRNO__INVALID_PAREN; - goto fail; - } - if (current_exp) { - show_error(error_str, - "Open paren can not come after expression"); - ret = TEP_ERRNO__INVALID_PAREN; - goto fail; - } - - ret = process_filter(event, &arg, error_str, 0); - if (ret != TEP_ERRNO__UNBALANCED_PAREN) { - if (ret == 0) { - show_error(error_str, - "Unbalanced number of '('"); - ret = TEP_ERRNO__UNBALANCED_PAREN; - } - goto fail; - } - ret = 0; - - /* A not wants just one expression */ - if (not) { - if (current_op) - goto fail_syntax; - *parg = arg; - return 0; - } - - if (current_op) - ret = add_right(current_op, arg, error_str); - else - current_exp = arg; - - if (ret < 0) - goto fail; - - } else { /* ')' */ - if (!current_op && !current_exp) - goto fail_syntax; - - /* Make sure everything is finished at this level */ - if (current_exp && !check_op_done(current_exp)) - goto fail_syntax; - if (current_op && !check_op_done(current_op)) - goto fail_syntax; - - if (current_op) - *parg = current_op; - else - *parg = current_exp; - free(token); - return TEP_ERRNO__UNBALANCED_PAREN; - } - break; - - case TEP_EVENT_OP: - op_type = process_op(token, &btype, &ctype, &etype); - - /* All expect a left arg except for NOT */ - switch (op_type) { - case OP_BOOL: - /* Logic ops need a left expression */ - if (!current_exp && !current_op) - goto fail_syntax; - /* fall through */ - case OP_NOT: - /* logic only processes ops and exp */ - if (left_item) - goto fail_syntax; - break; - case OP_EXP: - case OP_CMP: - if (!left_item) - goto fail_syntax; - break; - case OP_NONE: - show_error(error_str, - "Unknown op token %s", token); - ret = TEP_ERRNO__UNKNOWN_TOKEN; - goto fail; - } - - ret = 0; - switch (op_type) { - case OP_BOOL: - arg = create_arg_op(btype); - if (arg == NULL) - goto fail_alloc; - if (current_op) - ret = add_left(arg, current_op); - else - ret = add_left(arg, current_exp); - current_op = arg; - current_exp = NULL; - break; - - case OP_NOT: - arg = create_arg_op(btype); - if (arg == NULL) - goto fail_alloc; - if (current_op) - ret = add_right(current_op, arg, error_str); - if (ret < 0) - goto fail; - current_exp = arg; - ret = process_filter(event, &arg, error_str, 1); - if (ret < 0) - goto fail; - ret = add_right(current_exp, arg, error_str); - if (ret < 0) - goto fail; - break; - - case OP_EXP: - case OP_CMP: - if (op_type == OP_EXP) - arg = create_arg_exp(etype); - else - arg = create_arg_cmp(ctype); - if (arg == NULL) - goto fail_alloc; - - if (current_op) - ret = add_right(current_op, arg, error_str); - if (ret < 0) - goto fail; - ret = add_left(arg, left_item); - if (ret < 0) { - arg = NULL; - goto fail_syntax; - } - current_exp = arg; - break; - default: - break; - } - arg = NULL; - if (ret < 0) - goto fail_syntax; - break; - case TEP_EVENT_NONE: - break; - case TEP_EVENT_ERROR: - goto fail_alloc; - default: - goto fail_syntax; - } - } while (type != TEP_EVENT_NONE); - - if (!current_op && !current_exp) - goto fail_syntax; - - if (!current_op) - current_op = current_exp; - - ret = collapse_tree(current_op, parg, error_str); - /* collapse_tree() may free current_op, and updates parg accordingly */ - current_op = NULL; - if (ret < 0) - goto fail; - - free(token); - return 0; - - fail_alloc: - show_error(error_str, "failed to allocate filter arg"); - ret = TEP_ERRNO__MEM_ALLOC_FAILED; - goto fail; - fail_syntax: - show_error(error_str, "Syntax error"); - ret = TEP_ERRNO__SYNTAX_ERROR; - fail: - free_arg(current_op); - free_arg(current_exp); - free_arg(arg); - free(token); - return ret; -} - -static enum tep_errno -process_event(struct tep_event *event, const char *filter_str, - struct tep_filter_arg **parg, char *error_str) -{ - int ret; - - tep_buffer_init(filter_str, strlen(filter_str)); - - ret = process_filter(event, parg, error_str, 0); - if (ret < 0) - return ret; - - /* If parg is NULL, then make it into FALSE */ - if (!*parg) { - *parg = allocate_arg(); - if (*parg == NULL) - return TEP_ERRNO__MEM_ALLOC_FAILED; - - (*parg)->type = TEP_FILTER_ARG_BOOLEAN; - (*parg)->boolean.value = TEP_FILTER_FALSE; - } - - return 0; -} - -static enum tep_errno -filter_event(struct tep_event_filter *filter, struct tep_event *event, - const char *filter_str, char *error_str) -{ - struct tep_filter_type *filter_type; - struct tep_filter_arg *arg; - enum tep_errno ret; - - if (filter_str) { - ret = process_event(event, filter_str, &arg, error_str); - if (ret < 0) - return ret; - - } else { - /* just add a TRUE arg */ - arg = allocate_arg(); - if (arg == NULL) - return TEP_ERRNO__MEM_ALLOC_FAILED; - - arg->type = TEP_FILTER_ARG_BOOLEAN; - arg->boolean.value = TEP_FILTER_TRUE; - } - - filter_type = add_filter_type(filter, event->id); - if (filter_type == NULL) { - free_arg(arg); - return TEP_ERRNO__MEM_ALLOC_FAILED; - } - - if (filter_type->filter) - free_arg(filter_type->filter); - filter_type->filter = arg; - - return 0; -} - -static void filter_init_error_buf(struct tep_event_filter *filter) -{ - /* clear buffer to reset show error */ - tep_buffer_init("", 0); - filter->error_buffer[0] = '\0'; -} - -/** - * tep_filter_add_filter_str - add a new filter - * @filter: the event filter to add to - * @filter_str: the filter string that contains the filter - * - * Returns 0 if the filter was successfully added or a - * negative error code. Use tep_filter_strerror() to see - * actual error message in case of error. - */ -enum tep_errno tep_filter_add_filter_str(struct tep_event_filter *filter, - const char *filter_str) -{ - struct tep_handle *tep = filter->tep; - struct event_list *event; - struct event_list *events = NULL; - const char *filter_start; - const char *next_event; - char *this_event; - char *event_name = NULL; - char *sys_name = NULL; - char *sp; - enum tep_errno rtn = 0; /* TEP_ERRNO__SUCCESS */ - int len; - int ret; - - filter_init_error_buf(filter); - - filter_start = strchr(filter_str, ':'); - if (filter_start) - len = filter_start - filter_str; - else - len = strlen(filter_str); - - do { - next_event = strchr(filter_str, ','); - if (next_event && - (!filter_start || next_event < filter_start)) - len = next_event - filter_str; - else if (filter_start) - len = filter_start - filter_str; - else - len = strlen(filter_str); - - this_event = malloc(len + 1); - if (this_event == NULL) { - /* This can only happen when events is NULL, but still */ - free_events(events); - return TEP_ERRNO__MEM_ALLOC_FAILED; - } - memcpy(this_event, filter_str, len); - this_event[len] = 0; - - if (next_event) - next_event++; - - filter_str = next_event; - - sys_name = strtok_r(this_event, "/", &sp); - event_name = strtok_r(NULL, "/", &sp); - - if (!sys_name) { - /* This can only happen when events is NULL, but still */ - free_events(events); - free(this_event); - return TEP_ERRNO__FILTER_NOT_FOUND; - } - - /* Find this event */ - ret = find_event(tep, &events, strim(sys_name), strim(event_name)); - if (ret < 0) { - free_events(events); - free(this_event); - return ret; - } - free(this_event); - } while (filter_str); - - /* Skip the ':' */ - if (filter_start) - filter_start++; - - /* filter starts here */ - for (event = events; event; event = event->next) { - ret = filter_event(filter, event->event, filter_start, - filter->error_buffer); - /* Failures are returned if a parse error happened */ - if (ret < 0) - rtn = ret; - - if (ret >= 0 && tep->test_filters) { - char *test; - test = tep_filter_make_string(filter, event->event->id); - if (test) { - printf(" '%s: %s'\n", event->event->name, test); - free(test); - } - } - } - - free_events(events); - - return rtn; -} - -static void free_filter_type(struct tep_filter_type *filter_type) -{ - free_arg(filter_type->filter); -} - -/** - * tep_filter_strerror - fill error message in a buffer - * @filter: the event filter contains error - * @err: the error code - * @buf: the buffer to be filled in - * @buflen: the size of the buffer - * - * Returns 0 if message was filled successfully, -1 if error - */ -int tep_filter_strerror(struct tep_event_filter *filter, enum tep_errno err, - char *buf, size_t buflen) -{ - if (err <= __TEP_ERRNO__START || err >= __TEP_ERRNO__END) - return -1; - - if (strlen(filter->error_buffer) > 0) { - size_t len = snprintf(buf, buflen, "%s", filter->error_buffer); - - if (len > buflen) - return -1; - return 0; - } - - return tep_strerror(filter->tep, err, buf, buflen); -} - -/** - * tep_filter_remove_event - remove a filter for an event - * @filter: the event filter to remove from - * @event_id: the event to remove a filter for - * - * Removes the filter saved for an event defined by @event_id - * from the @filter. - * - * Returns 1: if an event was removed - * 0: if the event was not found - */ -int tep_filter_remove_event(struct tep_event_filter *filter, - int event_id) -{ - struct tep_filter_type *filter_type; - unsigned long len; - - if (!filter->filters) - return 0; - - filter_type = find_filter_type(filter, event_id); - - if (!filter_type) - return 0; - - free_filter_type(filter_type); - - /* The filter_type points into the event_filters array */ - len = (unsigned long)(filter->event_filters + filter->filters) - - (unsigned long)(filter_type + 1); - - memmove(filter_type, filter_type + 1, len); - filter->filters--; - - memset(&filter->event_filters[filter->filters], 0, - sizeof(*filter_type)); - - return 1; -} - -/** - * tep_filter_reset - clear all filters in a filter - * @filter: the event filter to reset - * - * Removes all filters from a filter and resets it. - */ -void tep_filter_reset(struct tep_event_filter *filter) -{ - int i; - - for (i = 0; i < filter->filters; i++) - free_filter_type(&filter->event_filters[i]); - - free(filter->event_filters); - filter->filters = 0; - filter->event_filters = NULL; -} - -void tep_filter_free(struct tep_event_filter *filter) -{ - tep_unref(filter->tep); - - tep_filter_reset(filter); - - free(filter); -} - -static char *arg_to_str(struct tep_event_filter *filter, struct tep_filter_arg *arg); - -static int copy_filter_type(struct tep_event_filter *filter, - struct tep_event_filter *source, - struct tep_filter_type *filter_type) -{ - struct tep_filter_arg *arg; - struct tep_event *event; - const char *sys; - const char *name; - char *str; - - /* Can't assume that the tep's are the same */ - sys = filter_type->event->system; - name = filter_type->event->name; - event = tep_find_event_by_name(filter->tep, sys, name); - if (!event) - return -1; - - str = arg_to_str(source, filter_type->filter); - if (!str) - return -1; - - if (strcmp(str, "TRUE") == 0 || strcmp(str, "FALSE") == 0) { - /* Add trivial event */ - arg = allocate_arg(); - if (arg == NULL) { - free(str); - return -1; - } - - arg->type = TEP_FILTER_ARG_BOOLEAN; - if (strcmp(str, "TRUE") == 0) - arg->boolean.value = 1; - else - arg->boolean.value = 0; - - filter_type = add_filter_type(filter, event->id); - if (filter_type == NULL) { - free(str); - free_arg(arg); - return -1; - } - - filter_type->filter = arg; - - free(str); - return 0; - } - - filter_event(filter, event, str, NULL); - free(str); - - return 0; -} - -/** - * tep_filter_copy - copy a filter using another filter - * @dest - the filter to copy to - * @source - the filter to copy from - * - * Returns 0 on success and -1 if not all filters were copied - */ -int tep_filter_copy(struct tep_event_filter *dest, struct tep_event_filter *source) -{ - int ret = 0; - int i; - - tep_filter_reset(dest); - - for (i = 0; i < source->filters; i++) { - if (copy_filter_type(dest, source, &source->event_filters[i])) - ret = -1; - } - return ret; -} - -static int test_filter(struct tep_event *event, struct tep_filter_arg *arg, - struct tep_record *record, enum tep_errno *err); - -static const char * -get_comm(struct tep_event *event, struct tep_record *record) -{ - const char *comm; - int pid; - - pid = tep_data_pid(event->tep, record); - comm = tep_data_comm_from_pid(event->tep, pid); - return comm; -} - -static unsigned long long -get_value(struct tep_event *event, - struct tep_format_field *field, struct tep_record *record) -{ - unsigned long long val; - - /* Handle our dummy "comm" field */ - if (field == &comm) { - const char *name; - - name = get_comm(event, record); - return (unsigned long)name; - } - - /* Handle our dummy "cpu" field */ - if (field == &cpu) - return record->cpu; - - tep_read_number_field(field, record->data, &val); - - if (!(field->flags & TEP_FIELD_IS_SIGNED)) - return val; - - switch (field->size) { - case 1: - return (char)val; - case 2: - return (short)val; - case 4: - return (int)val; - case 8: - return (long long)val; - } - return val; -} - -static unsigned long long -get_arg_value(struct tep_event *event, struct tep_filter_arg *arg, - struct tep_record *record, enum tep_errno *err); - -static unsigned long long -get_exp_value(struct tep_event *event, struct tep_filter_arg *arg, - struct tep_record *record, enum tep_errno *err) -{ - unsigned long long lval, rval; - - lval = get_arg_value(event, arg->exp.left, record, err); - rval = get_arg_value(event, arg->exp.right, record, err); - - if (*err) { - /* - * There was an error, no need to process anymore. - */ - return 0; - } - - switch (arg->exp.type) { - case TEP_FILTER_EXP_ADD: - return lval + rval; - - case TEP_FILTER_EXP_SUB: - return lval - rval; - - case TEP_FILTER_EXP_MUL: - return lval * rval; - - case TEP_FILTER_EXP_DIV: - return lval / rval; - - case TEP_FILTER_EXP_MOD: - return lval % rval; - - case TEP_FILTER_EXP_RSHIFT: - return lval >> rval; - - case TEP_FILTER_EXP_LSHIFT: - return lval << rval; - - case TEP_FILTER_EXP_AND: - return lval & rval; - - case TEP_FILTER_EXP_OR: - return lval | rval; - - case TEP_FILTER_EXP_XOR: - return lval ^ rval; - - case TEP_FILTER_EXP_NOT: - default: - if (!*err) - *err = TEP_ERRNO__INVALID_EXP_TYPE; - } - return 0; -} - -static unsigned long long -get_arg_value(struct tep_event *event, struct tep_filter_arg *arg, - struct tep_record *record, enum tep_errno *err) -{ - switch (arg->type) { - case TEP_FILTER_ARG_FIELD: - return get_value(event, arg->field.field, record); - - case TEP_FILTER_ARG_VALUE: - if (arg->value.type != TEP_FILTER_NUMBER) { - if (!*err) - *err = TEP_ERRNO__NOT_A_NUMBER; - } - return arg->value.val; - - case TEP_FILTER_ARG_EXP: - return get_exp_value(event, arg, record, err); - - default: - if (!*err) - *err = TEP_ERRNO__INVALID_ARG_TYPE; - } - return 0; -} - -static int test_num(struct tep_event *event, struct tep_filter_arg *arg, - struct tep_record *record, enum tep_errno *err) -{ - unsigned long long lval, rval; - - lval = get_arg_value(event, arg->num.left, record, err); - rval = get_arg_value(event, arg->num.right, record, err); - - if (*err) { - /* - * There was an error, no need to process anymore. - */ - return 0; - } - - switch (arg->num.type) { - case TEP_FILTER_CMP_EQ: - return lval == rval; - - case TEP_FILTER_CMP_NE: - return lval != rval; - - case TEP_FILTER_CMP_GT: - return lval > rval; - - case TEP_FILTER_CMP_LT: - return lval < rval; - - case TEP_FILTER_CMP_GE: - return lval >= rval; - - case TEP_FILTER_CMP_LE: - return lval <= rval; - - default: - if (!*err) - *err = TEP_ERRNO__ILLEGAL_INTEGER_CMP; - return 0; - } -} - -static const char *get_field_str(struct tep_filter_arg *arg, struct tep_record *record) -{ - struct tep_event *event; - struct tep_handle *tep; - unsigned long long addr; - const char *val = NULL; - unsigned int size; - char hex[64]; - - /* If the field is not a string convert it */ - if (arg->str.field->flags & TEP_FIELD_IS_STRING) { - val = record->data + arg->str.field->offset; - size = arg->str.field->size; - - if (arg->str.field->flags & TEP_FIELD_IS_DYNAMIC) { - addr = *(unsigned int *)val; - val = record->data + (addr & 0xffff); - size = addr >> 16; - } - - /* - * We need to copy the data since we can't be sure the field - * is null terminated. - */ - if (*(val + size - 1)) { - /* copy it */ - memcpy(arg->str.buffer, val, arg->str.field->size); - /* the buffer is already NULL terminated */ - val = arg->str.buffer; - } - - } else { - event = arg->str.field->event; - tep = event->tep; - addr = get_value(event, arg->str.field, record); - - if (arg->str.field->flags & (TEP_FIELD_IS_POINTER | TEP_FIELD_IS_LONG)) - /* convert to a kernel symbol */ - val = tep_find_function(tep, addr); - - if (val == NULL) { - /* just use the hex of the string name */ - snprintf(hex, 64, "0x%llx", addr); - val = hex; - } - } - - return val; -} - -static int test_str(struct tep_event *event, struct tep_filter_arg *arg, - struct tep_record *record, enum tep_errno *err) -{ - const char *val; - - if (arg->str.field == &comm) - val = get_comm(event, record); - else - val = get_field_str(arg, record); - - switch (arg->str.type) { - case TEP_FILTER_CMP_MATCH: - return strcmp(val, arg->str.val) == 0; - - case TEP_FILTER_CMP_NOT_MATCH: - return strcmp(val, arg->str.val) != 0; - - case TEP_FILTER_CMP_REGEX: - /* Returns zero on match */ - return !regexec(&arg->str.reg, val, 0, NULL, 0); - - case TEP_FILTER_CMP_NOT_REGEX: - return regexec(&arg->str.reg, val, 0, NULL, 0); - - default: - if (!*err) - *err = TEP_ERRNO__ILLEGAL_STRING_CMP; - return 0; - } -} - -static int test_op(struct tep_event *event, struct tep_filter_arg *arg, - struct tep_record *record, enum tep_errno *err) -{ - switch (arg->op.type) { - case TEP_FILTER_OP_AND: - return test_filter(event, arg->op.left, record, err) && - test_filter(event, arg->op.right, record, err); - - case TEP_FILTER_OP_OR: - return test_filter(event, arg->op.left, record, err) || - test_filter(event, arg->op.right, record, err); - - case TEP_FILTER_OP_NOT: - return !test_filter(event, arg->op.right, record, err); - - default: - if (!*err) - *err = TEP_ERRNO__INVALID_OP_TYPE; - return 0; - } -} - -static int test_filter(struct tep_event *event, struct tep_filter_arg *arg, - struct tep_record *record, enum tep_errno *err) -{ - if (*err) { - /* - * There was an error, no need to process anymore. - */ - return 0; - } - - switch (arg->type) { - case TEP_FILTER_ARG_BOOLEAN: - /* easy case */ - return arg->boolean.value; - - case TEP_FILTER_ARG_OP: - return test_op(event, arg, record, err); - - case TEP_FILTER_ARG_NUM: - return test_num(event, arg, record, err); - - case TEP_FILTER_ARG_STR: - return test_str(event, arg, record, err); - - case TEP_FILTER_ARG_EXP: - case TEP_FILTER_ARG_VALUE: - case TEP_FILTER_ARG_FIELD: - /* - * Expressions, fields and values evaluate - * to true if they return non zero - */ - return !!get_arg_value(event, arg, record, err); - - default: - if (!*err) - *err = TEP_ERRNO__INVALID_ARG_TYPE; - return 0; - } -} - -/** - * tep_event_filtered - return true if event has filter - * @filter: filter struct with filter information - * @event_id: event id to test if filter exists - * - * Returns 1 if filter found for @event_id - * otherwise 0; - */ -int tep_event_filtered(struct tep_event_filter *filter, int event_id) -{ - struct tep_filter_type *filter_type; - - if (!filter->filters) - return 0; - - filter_type = find_filter_type(filter, event_id); - - return filter_type ? 1 : 0; -} - -/** - * tep_filter_match - test if a record matches a filter - * @filter: filter struct with filter information - * @record: the record to test against the filter - * - * Returns: match result or error code (prefixed with TEP_ERRNO__) - * FILTER_MATCH - filter found for event and @record matches - * FILTER_MISS - filter found for event and @record does not match - * FILTER_NOT_FOUND - no filter found for @record's event - * NO_FILTER - if no filters exist - * otherwise - error occurred during test - */ -enum tep_errno tep_filter_match(struct tep_event_filter *filter, - struct tep_record *record) -{ - struct tep_handle *tep = filter->tep; - struct tep_filter_type *filter_type; - int event_id; - int ret; - enum tep_errno err = 0; - - filter_init_error_buf(filter); - - if (!filter->filters) - return TEP_ERRNO__NO_FILTER; - - event_id = tep_data_type(tep, record); - - filter_type = find_filter_type(filter, event_id); - if (!filter_type) - return TEP_ERRNO__FILTER_NOT_FOUND; - - ret = test_filter(filter_type->event, filter_type->filter, record, &err); - if (err) - return err; - - return ret ? TEP_ERRNO__FILTER_MATCH : TEP_ERRNO__FILTER_MISS; -} - -static char *op_to_str(struct tep_event_filter *filter, struct tep_filter_arg *arg) -{ - char *str = NULL; - char *left = NULL; - char *right = NULL; - char *op = NULL; - int left_val = -1; - int right_val = -1; - int val; - - switch (arg->op.type) { - case TEP_FILTER_OP_AND: - op = "&&"; - /* fall through */ - case TEP_FILTER_OP_OR: - if (!op) - op = "||"; - - left = arg_to_str(filter, arg->op.left); - right = arg_to_str(filter, arg->op.right); - if (!left || !right) - break; - - /* Try to consolidate boolean values */ - if (strcmp(left, "TRUE") == 0) - left_val = 1; - else if (strcmp(left, "FALSE") == 0) - left_val = 0; - - if (strcmp(right, "TRUE") == 0) - right_val = 1; - else if (strcmp(right, "FALSE") == 0) - right_val = 0; - - if (left_val >= 0) { - if ((arg->op.type == TEP_FILTER_OP_AND && !left_val) || - (arg->op.type == TEP_FILTER_OP_OR && left_val)) { - /* Just return left value */ - str = left; - left = NULL; - break; - } - if (right_val >= 0) { - /* just evaluate this. */ - val = 0; - switch (arg->op.type) { - case TEP_FILTER_OP_AND: - val = left_val && right_val; - break; - case TEP_FILTER_OP_OR: - val = left_val || right_val; - break; - default: - break; - } - if (asprintf(&str, val ? "TRUE" : "FALSE") < 0) - str = NULL; - break; - } - } - if (right_val >= 0) { - if ((arg->op.type == TEP_FILTER_OP_AND && !right_val) || - (arg->op.type == TEP_FILTER_OP_OR && right_val)) { - /* Just return right value */ - str = right; - right = NULL; - break; - } - /* The right value is meaningless */ - str = left; - left = NULL; - break; - } - - if (asprintf(&str, "(%s) %s (%s)", left, op, right) < 0) - str = NULL; - break; - - case TEP_FILTER_OP_NOT: - op = "!"; - right = arg_to_str(filter, arg->op.right); - if (!right) - break; - - /* See if we can consolidate */ - if (strcmp(right, "TRUE") == 0) - right_val = 1; - else if (strcmp(right, "FALSE") == 0) - right_val = 0; - if (right_val >= 0) { - /* just return the opposite */ - if (asprintf(&str, right_val ? "FALSE" : "TRUE") < 0) - str = NULL; - break; - } - if (asprintf(&str, "%s(%s)", op, right) < 0) - str = NULL; - break; - - default: - /* ?? */ - break; - } - free(left); - free(right); - return str; -} - -static char *val_to_str(struct tep_event_filter *filter, struct tep_filter_arg *arg) -{ - char *str = NULL; - - if (asprintf(&str, "%lld", arg->value.val) < 0) - str = NULL; - - return str; -} - -static char *field_to_str(struct tep_event_filter *filter, struct tep_filter_arg *arg) -{ - return strdup(arg->field.field->name); -} - -static char *exp_to_str(struct tep_event_filter *filter, struct tep_filter_arg *arg) -{ - char *lstr; - char *rstr; - char *op; - char *str = NULL; - - lstr = arg_to_str(filter, arg->exp.left); - rstr = arg_to_str(filter, arg->exp.right); - if (!lstr || !rstr) - goto out; - - switch (arg->exp.type) { - case TEP_FILTER_EXP_ADD: - op = "+"; - break; - case TEP_FILTER_EXP_SUB: - op = "-"; - break; - case TEP_FILTER_EXP_MUL: - op = "*"; - break; - case TEP_FILTER_EXP_DIV: - op = "/"; - break; - case TEP_FILTER_EXP_MOD: - op = "%"; - break; - case TEP_FILTER_EXP_RSHIFT: - op = ">>"; - break; - case TEP_FILTER_EXP_LSHIFT: - op = "<<"; - break; - case TEP_FILTER_EXP_AND: - op = "&"; - break; - case TEP_FILTER_EXP_OR: - op = "|"; - break; - case TEP_FILTER_EXP_XOR: - op = "^"; - break; - default: - op = "[ERROR IN EXPRESSION TYPE]"; - break; - } - - if (asprintf(&str, "%s %s %s", lstr, op, rstr) < 0) - str = NULL; -out: - free(lstr); - free(rstr); - - return str; -} - -static char *num_to_str(struct tep_event_filter *filter, struct tep_filter_arg *arg) -{ - char *lstr; - char *rstr; - char *str = NULL; - char *op = NULL; - - lstr = arg_to_str(filter, arg->num.left); - rstr = arg_to_str(filter, arg->num.right); - if (!lstr || !rstr) - goto out; - - switch (arg->num.type) { - case TEP_FILTER_CMP_EQ: - op = "=="; - /* fall through */ - case TEP_FILTER_CMP_NE: - if (!op) - op = "!="; - /* fall through */ - case TEP_FILTER_CMP_GT: - if (!op) - op = ">"; - /* fall through */ - case TEP_FILTER_CMP_LT: - if (!op) - op = "<"; - /* fall through */ - case TEP_FILTER_CMP_GE: - if (!op) - op = ">="; - /* fall through */ - case TEP_FILTER_CMP_LE: - if (!op) - op = "<="; - - if (asprintf(&str, "%s %s %s", lstr, op, rstr) < 0) - str = NULL; - break; - - default: - /* ?? */ - break; - } - -out: - free(lstr); - free(rstr); - return str; -} - -static char *str_to_str(struct tep_event_filter *filter, struct tep_filter_arg *arg) -{ - char *str = NULL; - char *op = NULL; - - switch (arg->str.type) { - case TEP_FILTER_CMP_MATCH: - op = "=="; - /* fall through */ - case TEP_FILTER_CMP_NOT_MATCH: - if (!op) - op = "!="; - /* fall through */ - case TEP_FILTER_CMP_REGEX: - if (!op) - op = "=~"; - /* fall through */ - case TEP_FILTER_CMP_NOT_REGEX: - if (!op) - op = "!~"; - - if (asprintf(&str, "%s %s \"%s\"", - arg->str.field->name, op, arg->str.val) < 0) - str = NULL; - break; - - default: - /* ?? */ - break; - } - return str; -} - -static char *arg_to_str(struct tep_event_filter *filter, struct tep_filter_arg *arg) -{ - char *str = NULL; - - switch (arg->type) { - case TEP_FILTER_ARG_BOOLEAN: - if (asprintf(&str, arg->boolean.value ? "TRUE" : "FALSE") < 0) - str = NULL; - return str; - - case TEP_FILTER_ARG_OP: - return op_to_str(filter, arg); - - case TEP_FILTER_ARG_NUM: - return num_to_str(filter, arg); - - case TEP_FILTER_ARG_STR: - return str_to_str(filter, arg); - - case TEP_FILTER_ARG_VALUE: - return val_to_str(filter, arg); - - case TEP_FILTER_ARG_FIELD: - return field_to_str(filter, arg); - - case TEP_FILTER_ARG_EXP: - return exp_to_str(filter, arg); - - default: - /* ?? */ - return NULL; - } - -} - -/** - * tep_filter_make_string - return a string showing the filter - * @filter: filter struct with filter information - * @event_id: the event id to return the filter string with - * - * Returns a string that displays the filter contents. - * This string must be freed with free(str). - * NULL is returned if no filter is found or allocation failed. - */ -char * -tep_filter_make_string(struct tep_event_filter *filter, int event_id) -{ - struct tep_filter_type *filter_type; - - if (!filter->filters) - return NULL; - - filter_type = find_filter_type(filter, event_id); - - if (!filter_type) - return NULL; - - return arg_to_str(filter, filter_type->filter); -} - -/** - * tep_filter_compare - compare two filters and return if they are the same - * @filter1: Filter to compare with @filter2 - * @filter2: Filter to compare with @filter1 - * - * Returns: - * 1 if the two filters hold the same content. - * 0 if they do not. - */ -int tep_filter_compare(struct tep_event_filter *filter1, struct tep_event_filter *filter2) -{ - struct tep_filter_type *filter_type1; - struct tep_filter_type *filter_type2; - char *str1, *str2; - int result; - int i; - - /* Do the easy checks first */ - if (filter1->filters != filter2->filters) - return 0; - if (!filter1->filters && !filter2->filters) - return 1; - - /* - * Now take a look at each of the events to see if they have the same - * filters to them. - */ - for (i = 0; i < filter1->filters; i++) { - filter_type1 = &filter1->event_filters[i]; - filter_type2 = find_filter_type(filter2, filter_type1->event_id); - if (!filter_type2) - break; - if (filter_type1->filter->type != filter_type2->filter->type) - break; - /* The best way to compare complex filters is with strings */ - str1 = arg_to_str(filter1, filter_type1->filter); - str2 = arg_to_str(filter2, filter_type2->filter); - if (str1 && str2) - result = strcmp(str1, str2) != 0; - else - /* bail out if allocation fails */ - result = 1; - - free(str1); - free(str2); - if (result) - break; - } - - if (i < filter1->filters) - return 0; - return 1; -} - |