diff options
Diffstat (limited to 'tools/lib/traceevent/parse-filter.c')
| -rw-r--r-- | tools/lib/traceevent/parse-filter.c | 2261 | 
1 files changed, 2261 insertions, 0 deletions
| diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c new file mode 100644 index 00000000000..dfcfe2c131d --- /dev/null +++ b/tools/lib/traceevent/parse-filter.c @@ -0,0 +1,2261 @@ +/* + * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License (not later!) + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#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-utils.h" + +#define COMM "COMM" + +static struct format_field comm = { +	.name = "COMM", +}; + +struct event_list { +	struct event_list	*next; +	struct event_format	*event; +}; + +#define MAX_ERR_STR_SIZE 256 + +static void show_error(char **error_str, const char *fmt, ...) +{ +	unsigned long long index; +	const char *input; +	char *error; +	va_list ap; +	int len; +	int i; + +	if (!error_str) +		return; + +	input = pevent_get_input_buf(); +	index = pevent_get_input_buf_ptr(); +	len = input ? strlen(input) : 0; + +	error = malloc_or_die(MAX_ERR_STR_SIZE + (len*2) + 3); + +	if (len) { +		strcpy(error, input); +		error[len] = '\n'; +		for (i = 1; i < len && i < index; i++) +			error[len+i] = ' '; +		error[len + i] = '^'; +		error[len + i + 1] = '\n'; +		len += i+2; +	} + +	va_start(ap, fmt); +	vsnprintf(error + len, MAX_ERR_STR_SIZE, fmt, ap); +	va_end(ap); + +	*error_str = error; +} + +static void free_token(char *token) +{ +	pevent_free_token(token); +} + +static enum event_type read_token(char **tok) +{ +	enum event_type type; +	char *token = NULL; + +	do { +		free_token(token); +		type = pevent_read_token(&token); +	} while (type == EVENT_NEWLINE || type == EVENT_SPACE); + +	/* If token is = or ! check to see if the next char is ~ */ +	if (token && +	    (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && +	    pevent_peek_char() == '~') { +		/* append it */ +		*tok = malloc(3); +		sprintf(*tok, "%c%c", *token, '~'); +		free_token(token); +		/* Now remove the '~' from the buffer */ +		pevent_read_token(&token); +		free_token(token); +	} else +		*tok = token; + +	return type; +} + +static int filter_cmp(const void *a, const void *b) +{ +	const struct filter_type *ea = a; +	const struct 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 filter_type * +find_filter_type(struct event_filter *filter, int id) +{ +	struct filter_type *filter_type; +	struct 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 filter_type * +add_filter_type(struct event_filter *filter, int id) +{ +	struct filter_type *filter_type; +	int i; + +	filter_type = find_filter_type(filter, id); +	if (filter_type) +		return filter_type; + +	if (!filter->filters) +		filter->event_filters = +			malloc_or_die(sizeof(*filter->event_filters)); +	else { +		filter->event_filters = +			realloc(filter->event_filters, +				sizeof(*filter->event_filters) * +				(filter->filters + 1)); +		if (!filter->event_filters) +			die("Could not allocate filter"); +	} + +	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 = pevent_find_event(filter->pevent, id); +	filter_type->filter = NULL; + +	filter->filters++; + +	return filter_type; +} + +/** + * pevent_filter_alloc - create a new event filter + * @pevent: The pevent that this filter is associated with + */ +struct event_filter *pevent_filter_alloc(struct pevent *pevent) +{ +	struct event_filter *filter; + +	filter = malloc_or_die(sizeof(*filter)); +	memset(filter, 0, sizeof(*filter)); +	filter->pevent = pevent; +	pevent_ref(pevent); + +	return filter; +} + +static struct filter_arg *allocate_arg(void) +{ +	struct filter_arg *arg; + +	arg = malloc_or_die(sizeof(*arg)); +	memset(arg, 0, sizeof(*arg)); + +	return arg; +} + +static void free_arg(struct filter_arg *arg) +{ +	if (!arg) +		return; + +	switch (arg->type) { +	case FILTER_ARG_NONE: +	case FILTER_ARG_BOOLEAN: +	case FILTER_ARG_NUM: +		break; + +	case FILTER_ARG_STR: +		free(arg->str.val); +		regfree(&arg->str.reg); +		free(arg->str.buffer); +		break; + +	case FILTER_ARG_OP: +		free_arg(arg->op.left); +		free_arg(arg->op.right); +	default: +		break; +	} + +	free(arg); +} + +static void add_event(struct event_list **events, +		      struct event_format *event) +{ +	struct event_list *list; + +	list = malloc_or_die(sizeof(*list)); +	list->next = *events; +	*events = list; +	list->event = event; +} + +static int event_match(struct event_format *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 int +find_event(struct pevent *pevent, struct event_list **events, +	   char *sys_name, char *event_name) +{ +	struct event_format *event; +	regex_t ereg; +	regex_t sreg; +	int match = 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; +	} + +	reg = malloc_or_die(strlen(event_name) + 3); +	sprintf(reg, "^%s$", event_name); + +	ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); +	free(reg); + +	if (ret) +		return -1; + +	if (sys_name) { +		reg = malloc_or_die(strlen(sys_name) + 3); +		sprintf(reg, "^%s$", sys_name); +		ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); +		free(reg); +		if (ret) { +			regfree(&ereg); +			return -1; +		} +	} + +	for (i = 0; i < pevent->nr_events; i++) { +		event = pevent->events[i]; +		if (event_match(event, sys_name ? &sreg : NULL, &ereg)) { +			match = 1; +			add_event(events, event); +		} +	} + +	regfree(&ereg); +	if (sys_name) +		regfree(&sreg); + +	if (!match) +		return -1; + +	return 0; +} + +static void free_events(struct event_list *events) +{ +	struct event_list *event; + +	while (events) { +		event = events; +		events = events->next; +		free(event); +	} +} + +static struct filter_arg * +create_arg_item(struct event_format *event, const char *token, +		enum event_type type, char **error_str) +{ +	struct format_field *field; +	struct filter_arg *arg; + +	arg = allocate_arg(); + +	switch (type) { + +	case EVENT_SQUOTE: +	case EVENT_DQUOTE: +		arg->type = FILTER_ARG_VALUE; +		arg->value.type = +			type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR; +		arg->value.str = strdup(token); +		if (!arg->value.str) +			die("malloc string"); +		break; +	case EVENT_ITEM: +		/* if it is a number, then convert it */ +		if (isdigit(token[0])) { +			arg->type = FILTER_ARG_VALUE; +			arg->value.type = FILTER_NUMBER; +			arg->value.val = strtoull(token, NULL, 0); +			break; +		} +		/* Consider this a field */ +		field = pevent_find_any_field(event, token); +		if (!field) { +			if (strcmp(token, COMM) != 0) { +				/* not a field, Make it false */ +				arg->type = FILTER_ARG_BOOLEAN; +				arg->boolean.value = FILTER_FALSE; +				break; +			} +			/* If token is 'COMM' then it is special */ +			field = &comm; +		} +		arg->type = FILTER_ARG_FIELD; +		arg->field.field = field; +		break; +	default: +		free_arg(arg); +		show_error(error_str, "expected a value but found %s", +			   token); +		return NULL; +	} +	return arg; +} + +static struct filter_arg * +create_arg_op(enum filter_op_type btype) +{ +	struct filter_arg *arg; + +	arg = allocate_arg(); +	arg->type = FILTER_ARG_OP; +	arg->op.type = btype; + +	return arg; +} + +static struct filter_arg * +create_arg_exp(enum filter_exp_type etype) +{ +	struct filter_arg *arg; + +	arg = allocate_arg(); +	arg->type = FILTER_ARG_EXP; +	arg->op.type = etype; + +	return arg; +} + +static struct filter_arg * +create_arg_cmp(enum filter_exp_type etype) +{ +	struct filter_arg *arg; + +	arg = allocate_arg(); +	/* Use NUM and change if necessary */ +	arg->type = FILTER_ARG_NUM; +	arg->op.type = etype; + +	return arg; +} + +static int add_right(struct filter_arg *op, struct filter_arg *arg, +		     char **error_str) +{ +	struct filter_arg *left; +	char *str; +	int op_type; +	int ret; + +	switch (op->type) { +	case FILTER_ARG_EXP: +		if (op->exp.right) +			goto out_fail; +		op->exp.right = arg; +		break; + +	case FILTER_ARG_OP: +		if (op->op.right) +			goto out_fail; +		op->op.right = arg; +		break; + +	case FILTER_ARG_NUM: +		if (op->op.right) +			goto out_fail; +		/* +		 * The arg must be num, str, or field +		 */ +		switch (arg->type) { +		case FILTER_ARG_VALUE: +		case FILTER_ARG_FIELD: +			break; +		default: +			show_error(error_str, +				   "Illegal rvalue"); +			return -1; +		} + +		/* +		 * Depending on the type, we may need to +		 * convert this to a string or regex. +		 */ +		switch (arg->value.type) { +		case 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 != FILTER_CMP_REGEX && +			    op->num.type != FILTER_CMP_NOT_REGEX) { +				arg->value.type = FILTER_NUMBER; +				goto do_int; +			} +			/* fall through */ +		case 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 == FILTER_ARG_BOOLEAN) { +				free_arg(left); +				free_arg(arg); +				op->type = FILTER_ARG_BOOLEAN; +				op->boolean.value = FILTER_FALSE; +				break; +			} + +			/* Left arg must be a field */ +			if (left->type != FILTER_ARG_FIELD) { +				show_error(error_str, +					   "Illegal lvalue for string comparison"); +				return -1; +			} + +			/* Make sure this is a valid string compare */ +			switch (op_type) { +			case FILTER_CMP_EQ: +				op_type = FILTER_CMP_MATCH; +				break; +			case FILTER_CMP_NE: +				op_type = FILTER_CMP_NOT_MATCH; +				break; + +			case FILTER_CMP_REGEX: +			case 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 -1; +				} +				break; +			default: +				show_error(error_str, +					   "Illegal comparison for string"); +				return -1; +			} + +			op->type = FILTER_ARG_STR; +			op->str.type = op_type; +			op->str.field = left->field.field; +			op->str.val = strdup(str); +			if (!op->str.val) +				die("malloc string"); +			/* +			 * Need a buffer to copy data for tests +			 */ +			op->str.buffer = malloc_or_die(op->str.field->size + 1); +			/* 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 FILTER_NUMBER: + + do_int: +			switch (op->num.type) { +			case FILTER_CMP_REGEX: +			case FILTER_CMP_NOT_REGEX: +				show_error(error_str, +					   "Op not allowed with integers"); +				return -1; + +			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 -1; +} + +static struct filter_arg * +rotate_op_right(struct filter_arg *a, struct filter_arg *b) +{ +	struct filter_arg *arg; + +	arg = a->op.right; +	a->op.right = b; +	return arg; +} + +static int add_left(struct filter_arg *op, struct filter_arg *arg) +{ +	switch (op->type) { +	case FILTER_ARG_EXP: +		if (arg->type == FILTER_ARG_OP) +			arg = rotate_op_right(arg, op); +		op->exp.left = arg; +		break; + +	case FILTER_ARG_OP: +		op->op.left = arg; +		break; +	case FILTER_ARG_NUM: +		if (arg->type == FILTER_ARG_OP) +			arg = rotate_op_right(arg, op); + +		/* left arg of compares must be a field */ +		if (arg->type != FILTER_ARG_FIELD && +		    arg->type != FILTER_ARG_BOOLEAN) +			return -1; +		op->num.left = arg; +		break; +	default: +		return -1; +	} +	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 filter_op_type *btype, +			       enum filter_cmp_type *ctype, +			       enum filter_exp_type *etype) +{ +	*btype = FILTER_OP_NOT; +	*etype = FILTER_EXP_NONE; +	*ctype = FILTER_CMP_NONE; + +	if (strcmp(token, "&&") == 0) +		*btype = FILTER_OP_AND; +	else if (strcmp(token, "||") == 0) +		*btype = FILTER_OP_OR; +	else if (strcmp(token, "!") == 0) +		return OP_NOT; + +	if (*btype != FILTER_OP_NOT) +		return OP_BOOL; + +	/* Check for value expressions */ +	if (strcmp(token, "+") == 0) { +		*etype = FILTER_EXP_ADD; +	} else if (strcmp(token, "-") == 0) { +		*etype = FILTER_EXP_SUB; +	} else if (strcmp(token, "*") == 0) { +		*etype = FILTER_EXP_MUL; +	} else if (strcmp(token, "/") == 0) { +		*etype = FILTER_EXP_DIV; +	} else if (strcmp(token, "%") == 0) { +		*etype = FILTER_EXP_MOD; +	} else if (strcmp(token, ">>") == 0) { +		*etype = FILTER_EXP_RSHIFT; +	} else if (strcmp(token, "<<") == 0) { +		*etype = FILTER_EXP_LSHIFT; +	} else if (strcmp(token, "&") == 0) { +		*etype = FILTER_EXP_AND; +	} else if (strcmp(token, "|") == 0) { +		*etype = FILTER_EXP_OR; +	} else if (strcmp(token, "^") == 0) { +		*etype = FILTER_EXP_XOR; +	} else if (strcmp(token, "~") == 0) +		*etype = FILTER_EXP_NOT; + +	if (*etype != FILTER_EXP_NONE) +		return OP_EXP; + +	/* Check for compares */ +	if (strcmp(token, "==") == 0) +		*ctype = FILTER_CMP_EQ; +	else if (strcmp(token, "!=") == 0) +		*ctype = FILTER_CMP_NE; +	else if (strcmp(token, "<") == 0) +		*ctype = FILTER_CMP_LT; +	else if (strcmp(token, ">") == 0) +		*ctype = FILTER_CMP_GT; +	else if (strcmp(token, "<=") == 0) +		*ctype = FILTER_CMP_LE; +	else if (strcmp(token, ">=") == 0) +		*ctype = FILTER_CMP_GE; +	else if (strcmp(token, "=~") == 0) +		*ctype = FILTER_CMP_REGEX; +	else if (strcmp(token, "!~") == 0) +		*ctype = FILTER_CMP_NOT_REGEX; +	else +		return OP_NONE; + +	return OP_CMP; +} + +static int check_op_done(struct filter_arg *arg) +{ +	switch (arg->type) { +	case FILTER_ARG_EXP: +		return arg->exp.right != NULL; + +	case FILTER_ARG_OP: +		return arg->op.right != NULL; + +	case FILTER_ARG_NUM: +		return arg->num.right != NULL; + +	case FILTER_ARG_STR: +		/* A string conversion is always done */ +		return 1; + +	case 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, +}; + +void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, +		  struct filter_arg *arg) +{ +	struct filter_arg *other_child; +	struct filter_arg **ptr; + +	if (parent->type != FILTER_ARG_OP && +	    arg->type != FILTER_ARG_OP) +		die("can not reparent other than 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 +		die("Error in reparent op, find other child"); + +	/* 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; +	} + +	if (parent->op.right == old_child) +		ptr = &parent->op.right; +	else if (parent->op.left == old_child) +		ptr = &parent->op.left; +	else +		die("Error in reparent op"); +	*ptr = arg; + +	free_arg(old_child); +} + +enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) +{ +	enum filter_vals lval, rval; + +	switch (arg->type) { + +		/* bad case */ +	case FILTER_ARG_BOOLEAN: +		return FILTER_VAL_FALSE + arg->boolean.value; + +		/* good cases: */ +	case FILTER_ARG_STR: +	case FILTER_ARG_VALUE: +	case FILTER_ARG_FIELD: +		return FILTER_VAL_NORM; + +	case FILTER_ARG_EXP: +		lval = test_arg(arg, arg->exp.left); +		if (lval != FILTER_VAL_NORM) +			return lval; +		rval = test_arg(arg, arg->exp.right); +		if (rval != FILTER_VAL_NORM) +			return rval; +		return FILTER_VAL_NORM; + +	case FILTER_ARG_NUM: +		lval = test_arg(arg, arg->num.left); +		if (lval != FILTER_VAL_NORM) +			return lval; +		rval = test_arg(arg, arg->num.right); +		if (rval != FILTER_VAL_NORM) +			return rval; +		return FILTER_VAL_NORM; + +	case FILTER_ARG_OP: +		if (arg->op.type != FILTER_OP_NOT) { +			lval = test_arg(arg, arg->op.left); +			switch (lval) { +			case FILTER_VAL_NORM: +				break; +			case FILTER_VAL_TRUE: +				if (arg->op.type == FILTER_OP_OR) +					return FILTER_VAL_TRUE; +				rval = test_arg(arg, arg->op.right); +				if (rval != FILTER_VAL_NORM) +					return rval; + +				reparent_op_arg(parent, arg, arg->op.right); +				return FILTER_VAL_NORM; + +			case FILTER_VAL_FALSE: +				if (arg->op.type == FILTER_OP_AND) +					return FILTER_VAL_FALSE; +				rval = test_arg(arg, arg->op.right); +				if (rval != FILTER_VAL_NORM) +					return rval; + +				reparent_op_arg(parent, arg, arg->op.right); +				return FILTER_VAL_NORM; +			} +		} + +		rval = test_arg(arg, arg->op.right); +		switch (rval) { +		case FILTER_VAL_NORM: +			break; +		case FILTER_VAL_TRUE: +			if (arg->op.type == FILTER_OP_OR) +				return FILTER_VAL_TRUE; +			if (arg->op.type == FILTER_OP_NOT) +				return FILTER_VAL_FALSE; + +			reparent_op_arg(parent, arg, arg->op.left); +			return FILTER_VAL_NORM; + +		case FILTER_VAL_FALSE: +			if (arg->op.type == FILTER_OP_AND) +				return FILTER_VAL_FALSE; +			if (arg->op.type == FILTER_OP_NOT) +				return FILTER_VAL_TRUE; + +			reparent_op_arg(parent, arg, arg->op.left); +			return FILTER_VAL_NORM; +		} + +		return FILTER_VAL_NORM; +	default: +		die("bad arg in filter tree"); +	} +	return FILTER_VAL_NORM; +} + +/* Remove any unknown event fields */ +static struct filter_arg *collapse_tree(struct filter_arg *arg) +{ +	enum filter_vals ret; + +	ret = test_arg(arg, arg); +	switch (ret) { +	case FILTER_VAL_NORM: +		return arg; + +	case FILTER_VAL_TRUE: +	case FILTER_VAL_FALSE: +		free_arg(arg); +		arg = allocate_arg(); +		arg->type = FILTER_ARG_BOOLEAN; +		arg->boolean.value = ret == FILTER_VAL_TRUE; +	} + +	return arg; +} + +static int +process_filter(struct event_format *event, struct filter_arg **parg, +	       char **error_str, int not) +{ +	enum event_type type; +	char *token = NULL; +	struct filter_arg *current_op = NULL; +	struct filter_arg *current_exp = NULL; +	struct filter_arg *left_item = NULL; +	struct filter_arg *arg = NULL; +	enum op_type op_type; +	enum filter_op_type btype; +	enum filter_exp_type etype; +	enum filter_cmp_type ctype; +	int ret; + +	*parg = NULL; + +	do { +		free(token); +		type = read_token(&token); +		switch (type) { +		case EVENT_SQUOTE: +		case EVENT_DQUOTE: +		case EVENT_ITEM: +			arg = create_arg_item(event, token, type, error_str); +			if (!arg) +				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_print; +					free(token); +					*parg = current_exp; +					return 0; +				} +			} else +				goto fail_print; +			arg = NULL; +			break; + +		case EVENT_DELIM: +			if (*token == ',') { +				show_error(error_str, +					   "Illegal token ','"); +				goto fail; +			} + +			if (*token == '(') { +				if (left_item) { +					show_error(error_str, +						   "Open paren can not come after item"); +					goto fail; +				} +				if (current_exp) { +					show_error(error_str, +						   "Open paren can not come after expression"); +					goto fail; +				} + +				ret = process_filter(event, &arg, error_str, 0); +				if (ret != 1) { +					if (ret == 0) +						show_error(error_str, +							   "Unbalanced number of '('"); +					goto fail; +				} +				ret = 0; + +				/* A not wants just one expression */ +				if (not) { +					if (current_op) +						goto fail_print; +					*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_print; + +				/* Make sure everything is finished at this level */ +				if (current_exp && !check_op_done(current_exp)) +					goto fail_print; +				if (current_op && !check_op_done(current_op)) +					goto fail_print; + +				if (current_op) +					*parg = current_op; +				else +					*parg = current_exp; +				return 1; +			} +			break; + +		case 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_print; +				/* fall through */ +			case OP_NOT: +				/* logic only processes ops and exp */ +				if (left_item) +					goto fail_print; +				break; +			case OP_EXP: +			case OP_CMP: +				if (!left_item) +					goto fail_print; +				break; +			case OP_NONE: +				show_error(error_str, +					   "Unknown op token %s", token); +				goto fail; +			} + +			ret = 0; +			switch (op_type) { +			case OP_BOOL: +				arg = create_arg_op(btype); +				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 (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 (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_print; +				} +				current_exp = arg; +				break; +			default: +				break; +			} +			arg = NULL; +			if (ret < 0) +				goto fail_print; +			break; +		case EVENT_NONE: +			break; +		default: +			goto fail_print; +		} +	} while (type != EVENT_NONE); + +	if (!current_op && !current_exp) +		goto fail_print; + +	if (!current_op) +		current_op = current_exp; + +	current_op = collapse_tree(current_op); + +	*parg = current_op; + +	return 0; + + fail_print: +	show_error(error_str, "Syntax error"); + fail: +	free_arg(current_op); +	free_arg(current_exp); +	free_arg(arg); +	free(token); +	return -1; +} + +static int +process_event(struct event_format *event, const char *filter_str, +	      struct filter_arg **parg, char **error_str) +{ +	int ret; + +	pevent_buffer_init(filter_str, strlen(filter_str)); + +	ret = process_filter(event, parg, error_str, 0); +	if (ret == 1) { +		show_error(error_str, +			   "Unbalanced number of ')'"); +		return -1; +	} +	if (ret < 0) +		return ret; + +	/* If parg is NULL, then make it into FALSE */ +	if (!*parg) { +		*parg = allocate_arg(); +		(*parg)->type = FILTER_ARG_BOOLEAN; +		(*parg)->boolean.value = FILTER_FALSE; +	} + +	return 0; +} + +static int filter_event(struct event_filter *filter, +			struct event_format *event, +			const char *filter_str, char **error_str) +{ +	struct filter_type *filter_type; +	struct filter_arg *arg; +	int 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(); +		arg->type = FILTER_ARG_BOOLEAN; +		arg->boolean.value = FILTER_TRUE; +	} + +	filter_type = add_filter_type(filter, event->id); +	if (filter_type->filter) +		free_arg(filter_type->filter); +	filter_type->filter = arg; + +	return 0; +} + +/** + * pevent_filter_add_filter_str - add a new filter + * @filter: the event filter to add to + * @filter_str: the filter string that contains the filter + * @error_str: string containing reason for failed filter + * + * Returns 0 if the filter was successfully added + *   -1 if there was an error. + * + * On error, if @error_str points to a string pointer, + * it is set to the reason that the filter failed. + * This string must be freed with "free". + */ +int pevent_filter_add_filter_str(struct event_filter *filter, +				 const char *filter_str, +				 char **error_str) +{ +	struct pevent *pevent = filter->pevent; +	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; +	int rtn = 0; +	int len; +	int ret; + +	/* clear buffer to reset show error */ +	pevent_buffer_init("", 0); + +	if (error_str) +		*error_str = NULL; + +	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_or_die(len + 1); +		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) { +			show_error(error_str, "No filter found"); +			/* This can only happen when events is NULL, but still */ +			free_events(events); +			free(this_event); +			return -1; +		} + +		/* Find this event */ +		ret = find_event(pevent, &events, strim(sys_name), strim(event_name)); +		if (ret < 0) { +			if (event_name) +				show_error(error_str, +					   "No event found under '%s.%s'", +					   sys_name, event_name); +			else +				show_error(error_str, +					   "No event found under '%s'", +					   sys_name); +			free_events(events); +			free(this_event); +			return -1; +		} +		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, +				   error_str); +		/* Failures are returned if a parse error happened */ +		if (ret < 0) +			rtn = ret; + +		if (ret >= 0 && pevent->test_filters) { +			char *test; +			test = pevent_filter_make_string(filter, event->event->id); +			printf(" '%s: %s'\n", event->event->name, test); +			free(test); +		} +	} + +	free_events(events); + +	if (rtn >= 0 && pevent->test_filters) +		exit(0); + +	return rtn; +} + +static void free_filter_type(struct filter_type *filter_type) +{ +	free_arg(filter_type->filter); +} + +/** + * pevent_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 pevent_filter_remove_event(struct event_filter *filter, +			       int event_id) +{ +	struct 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; +} + +/** + * pevent_filter_reset - clear all filters in a filter + * @filter: the event filter to reset + * + * Removes all filters from a filter and resets it. + */ +void pevent_filter_reset(struct 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 pevent_filter_free(struct event_filter *filter) +{ +	pevent_unref(filter->pevent); + +	pevent_filter_reset(filter); + +	free(filter); +} + +static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg); + +static int copy_filter_type(struct event_filter *filter, +			     struct event_filter *source, +			     struct filter_type *filter_type) +{ +	struct filter_arg *arg; +	struct event_format *event; +	const char *sys; +	const char *name; +	char *str; + +	/* Can't assume that the pevent's are the same */ +	sys = filter_type->event->system; +	name = filter_type->event->name; +	event = pevent_find_event_by_name(filter->pevent, 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(); +		arg->type = 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); +		filter_type->filter = arg; + +		free(str); +		return 0; +	} + +	filter_event(filter, event, str, NULL); +	free(str); + +	return 0; +} + +/** + * pevent_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 pevent_filter_copy(struct event_filter *dest, struct event_filter *source) +{ +	int ret = 0; +	int i; + +	pevent_filter_reset(dest); + +	for (i = 0; i < source->filters; i++) { +		if (copy_filter_type(dest, source, &source->event_filters[i])) +			ret = -1; +	} +	return ret; +} + + +/** + * pevent_update_trivial - update the trivial filters with the given filter + * @dest - the filter to update + * @source - the filter as the source of the update + * @type - the type of trivial filter to update. + * + * Scan dest for trivial events matching @type to replace with the source. + * + * Returns 0 on success and -1 if there was a problem updating, but + *   events may have still been updated on error. + */ +int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, +			  enum filter_trivial_type type) +{ +	struct pevent *src_pevent; +	struct pevent *dest_pevent; +	struct event_format *event; +	struct filter_type *filter_type; +	struct filter_arg *arg; +	char *str; +	int i; + +	src_pevent = source->pevent; +	dest_pevent = dest->pevent; + +	/* Do nothing if either of the filters has nothing to filter */ +	if (!dest->filters || !source->filters) +		return 0; + +	for (i = 0; i < dest->filters; i++) { +		filter_type = &dest->event_filters[i]; +		arg = filter_type->filter; +		if (arg->type != FILTER_ARG_BOOLEAN) +			continue; +		if ((arg->boolean.value && type == FILTER_TRIVIAL_FALSE) || +		    (!arg->boolean.value && type == FILTER_TRIVIAL_TRUE)) +			continue; + +		event = filter_type->event; + +		if (src_pevent != dest_pevent) { +			/* do a look up */ +			event = pevent_find_event_by_name(src_pevent, +							  event->system, +							  event->name); +			if (!event) +				return -1; +		} + +		str = pevent_filter_make_string(source, event->id); +		if (!str) +			continue; + +		/* Don't bother if the filter is trivial too */ +		if (strcmp(str, "TRUE") != 0 && strcmp(str, "FALSE") != 0) +			filter_event(dest, event, str, NULL); +		free(str); +	} +	return 0; +} + +/** + * pevent_filter_clear_trivial - clear TRUE and FALSE filters + * @filter: the filter to remove trivial filters from + * @type: remove only true, false, or both + * + * Removes filters that only contain a TRUE or FALES boolean arg. + */ +void pevent_filter_clear_trivial(struct event_filter *filter, +				 enum filter_trivial_type type) +{ +	struct filter_type *filter_type; +	int count = 0; +	int *ids; +	int i; + +	if (!filter->filters) +		return; + +	/* +	 * Two steps, first get all ids with trivial filters. +	 *  then remove those ids. +	 */ +	for (i = 0; i < filter->filters; i++) { +		filter_type = &filter->event_filters[i]; +		if (filter_type->filter->type != FILTER_ARG_BOOLEAN) +			continue; +		switch (type) { +		case FILTER_TRIVIAL_FALSE: +			if (filter_type->filter->boolean.value) +				continue; +		case FILTER_TRIVIAL_TRUE: +			if (!filter_type->filter->boolean.value) +				continue; +		default: +			break; +		} +		if (count) +			ids = realloc(ids, sizeof(*ids) * (count + 1)); +		else +			ids = malloc(sizeof(*ids)); +		if (!ids) +			die("Can't allocate ids"); +		ids[count++] = filter_type->event_id; +	} + +	if (!count) +		return; + +	for (i = 0; i < count; i++) +		pevent_filter_remove_event(filter, ids[i]); + +	free(ids); +} + +/** + * pevent_filter_event_has_trivial - return true event contains trivial filter + * @filter: the filter with the information + * @event_id: the id of the event to test + * @type: trivial type to test for (TRUE, FALSE, EITHER) + * + * Returns 1 if the event contains a matching trivial type + *  otherwise 0. + */ +int pevent_filter_event_has_trivial(struct event_filter *filter, +				    int event_id, +				    enum filter_trivial_type type) +{ +	struct filter_type *filter_type; + +	if (!filter->filters) +		return 0; + +	filter_type = find_filter_type(filter, event_id); + +	if (!filter_type) +		return 0; + +	if (filter_type->filter->type != FILTER_ARG_BOOLEAN) +		return 0; + +	switch (type) { +	case FILTER_TRIVIAL_FALSE: +		return !filter_type->filter->boolean.value; + +	case FILTER_TRIVIAL_TRUE: +		return filter_type->filter->boolean.value; +	default: +		return 1; +	} +} + +static int test_filter(struct event_format *event, +		       struct filter_arg *arg, struct pevent_record *record); + +static const char * +get_comm(struct event_format *event, struct pevent_record *record) +{ +	const char *comm; +	int pid; + +	pid = pevent_data_pid(event->pevent, record); +	comm = pevent_data_comm_from_pid(event->pevent, pid); +	return comm; +} + +static unsigned long long +get_value(struct event_format *event, +	  struct format_field *field, struct pevent_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; +	} + +	pevent_read_number_field(field, record->data, &val); + +	if (!(field->flags & 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 event_format *event, struct filter_arg *arg, struct pevent_record *record); + +static unsigned long long +get_exp_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) +{ +	unsigned long long lval, rval; + +	lval = get_arg_value(event, arg->exp.left, record); +	rval = get_arg_value(event, arg->exp.right, record); + +	switch (arg->exp.type) { +	case FILTER_EXP_ADD: +		return lval + rval; + +	case FILTER_EXP_SUB: +		return lval - rval; + +	case FILTER_EXP_MUL: +		return lval * rval; + +	case FILTER_EXP_DIV: +		return lval / rval; + +	case FILTER_EXP_MOD: +		return lval % rval; + +	case FILTER_EXP_RSHIFT: +		return lval >> rval; + +	case FILTER_EXP_LSHIFT: +		return lval << rval; + +	case FILTER_EXP_AND: +		return lval & rval; + +	case FILTER_EXP_OR: +		return lval | rval; + +	case FILTER_EXP_XOR: +		return lval ^ rval; + +	case FILTER_EXP_NOT: +	default: +		die("error in exp"); +	} +	return 0; +} + +static unsigned long long +get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) +{ +	switch (arg->type) { +	case FILTER_ARG_FIELD: +		return get_value(event, arg->field.field, record); + +	case FILTER_ARG_VALUE: +		if (arg->value.type != FILTER_NUMBER) +			die("must have number field!"); +		return arg->value.val; + +	case FILTER_ARG_EXP: +		return get_exp_value(event, arg, record); + +	default: +		die("oops in filter"); +	} +	return 0; +} + +static int test_num(struct event_format *event, +		    struct filter_arg *arg, struct pevent_record *record) +{ +	unsigned long long lval, rval; + +	lval = get_arg_value(event, arg->num.left, record); +	rval = get_arg_value(event, arg->num.right, record); + +	switch (arg->num.type) { +	case FILTER_CMP_EQ: +		return lval == rval; + +	case FILTER_CMP_NE: +		return lval != rval; + +	case FILTER_CMP_GT: +		return lval > rval; + +	case FILTER_CMP_LT: +		return lval < rval; + +	case FILTER_CMP_GE: +		return lval >= rval; + +	case FILTER_CMP_LE: +		return lval <= rval; + +	default: +		/* ?? */ +		return 0; +	} +} + +static const char *get_field_str(struct filter_arg *arg, struct pevent_record *record) +{ +	const char *val = record->data + arg->str.field->offset; + +	/* +	 * We need to copy the data since we can't be sure the field +	 * is null terminated. +	 */ +	if (*(val + arg->str.field->size - 1)) { +		/* copy it */ +		memcpy(arg->str.buffer, val, arg->str.field->size); +		/* the buffer is already NULL terminated */ +		val = arg->str.buffer; +	} +	return val; +} + +static int test_str(struct event_format *event, +		    struct filter_arg *arg, struct pevent_record *record) +{ +	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 FILTER_CMP_MATCH: +		return strcmp(val, arg->str.val) == 0; + +	case FILTER_CMP_NOT_MATCH: +		return strcmp(val, arg->str.val) != 0; + +	case FILTER_CMP_REGEX: +		/* Returns zero on match */ +		return !regexec(&arg->str.reg, val, 0, NULL, 0); + +	case FILTER_CMP_NOT_REGEX: +		return regexec(&arg->str.reg, val, 0, NULL, 0); + +	default: +		/* ?? */ +		return 0; +	} +} + +static int test_op(struct event_format *event, +		   struct filter_arg *arg, struct pevent_record *record) +{ +	switch (arg->op.type) { +	case FILTER_OP_AND: +		return test_filter(event, arg->op.left, record) && +			test_filter(event, arg->op.right, record); + +	case FILTER_OP_OR: +		return test_filter(event, arg->op.left, record) || +			test_filter(event, arg->op.right, record); + +	case FILTER_OP_NOT: +		return !test_filter(event, arg->op.right, record); + +	default: +		/* ?? */ +		return 0; +	} +} + +static int test_filter(struct event_format *event, +		       struct filter_arg *arg, struct pevent_record *record) +{ +	switch (arg->type) { +	case FILTER_ARG_BOOLEAN: +		/* easy case */ +		return arg->boolean.value; + +	case FILTER_ARG_OP: +		return test_op(event, arg, record); + +	case FILTER_ARG_NUM: +		return test_num(event, arg, record); + +	case FILTER_ARG_STR: +		return test_str(event, arg, record); + +	case FILTER_ARG_EXP: +	case FILTER_ARG_VALUE: +	case FILTER_ARG_FIELD: +		/* +		 * Expressions, fields and values evaluate +		 * to true if they return non zero +		 */ +		return !!get_arg_value(event, arg, record); + +	default: +		die("oops!"); +		/* ?? */ +		return 0; +	} +} + +/** + * pevent_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 pevent_event_filtered(struct event_filter *filter, +			  int event_id) +{ +	struct filter_type *filter_type; + +	if (!filter->filters) +		return 0; + +	filter_type = find_filter_type(filter, event_id); + +	return filter_type ? 1 : 0; +} + +/** + * pevent_filter_match - test if a record matches a filter + * @filter: filter struct with filter information + * @record: the record to test against the filter + * + * Returns: + *  1 - filter found for event and @record matches + *  0 - filter found for event and @record does not match + * -1 - no filter found for @record's event + * -2 - if no filters exist + */ +int pevent_filter_match(struct event_filter *filter, +			struct pevent_record *record) +{ +	struct pevent *pevent = filter->pevent; +	struct filter_type *filter_type; +	int event_id; + +	if (!filter->filters) +		return FILTER_NONE; + +	event_id = pevent_data_type(pevent, record); + +	filter_type = find_filter_type(filter, event_id); + +	if (!filter_type) +		return FILTER_NOEXIST; + +	return test_filter(filter_type->event, filter_type->filter, record) ? +		FILTER_MATCH : FILTER_MISS; +} + +static char *op_to_str(struct event_filter *filter, struct 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; +	int len; + +	switch (arg->op.type) { +	case FILTER_OP_AND: +		op = "&&"; +		/* fall through */ +	case 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 == FILTER_OP_AND && !left_val) || +			    (arg->op.type == 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 FILTER_OP_AND: +					val = left_val && right_val; +					break; +				case FILTER_OP_OR: +					val = left_val || right_val; +					break; +				default: +					break; +				} +				str = malloc_or_die(6); +				if (val) +					strcpy(str, "TRUE"); +				else +					strcpy(str, "FALSE"); +				break; +			} +		} +		if (right_val >= 0) { +			if ((arg->op.type == FILTER_OP_AND && !right_val) || +			    (arg->op.type == FILTER_OP_OR && right_val)) { +				/* Just return right value */ +				str = right; +				right = NULL; +				break; +			} +			/* The right value is meaningless */ +			str = left; +			left = NULL; +			break; +		} + +		len = strlen(left) + strlen(right) + strlen(op) + 10; +		str = malloc_or_die(len); +		snprintf(str, len, "(%s) %s (%s)", +			 left, op, right); +		break; + +	case 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 */ +			str = malloc_or_die(6); +			if (right_val) +				strcpy(str, "FALSE"); +			else +				strcpy(str, "TRUE"); +			break; +		} +		len = strlen(right) + strlen(op) + 3; +		str = malloc_or_die(len); +		snprintf(str, len, "%s(%s)", op, right); +		break; + +	default: +		/* ?? */ +		break; +	} +	free(left); +	free(right); +	return str; +} + +static char *val_to_str(struct event_filter *filter, struct filter_arg *arg) +{ +	char *str; + +	str = malloc_or_die(30); + +	snprintf(str, 30, "%lld", arg->value.val); + +	return str; +} + +static char *field_to_str(struct event_filter *filter, struct filter_arg *arg) +{ +	return strdup(arg->field.field->name); +} + +static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) +{ +	char *lstr; +	char *rstr; +	char *op; +	char *str; +	int len; + +	lstr = arg_to_str(filter, arg->exp.left); +	rstr = arg_to_str(filter, arg->exp.right); + +	switch (arg->exp.type) { +	case FILTER_EXP_ADD: +		op = "+"; +		break; +	case FILTER_EXP_SUB: +		op = "-"; +		break; +	case FILTER_EXP_MUL: +		op = "*"; +		break; +	case FILTER_EXP_DIV: +		op = "/"; +		break; +	case FILTER_EXP_MOD: +		op = "%"; +		break; +	case FILTER_EXP_RSHIFT: +		op = ">>"; +		break; +	case FILTER_EXP_LSHIFT: +		op = "<<"; +		break; +	case FILTER_EXP_AND: +		op = "&"; +		break; +	case FILTER_EXP_OR: +		op = "|"; +		break; +	case FILTER_EXP_XOR: +		op = "^"; +		break; +	default: +		die("oops in exp"); +	} + +	len = strlen(op) + strlen(lstr) + strlen(rstr) + 4; +	str = malloc_or_die(len); +	snprintf(str, len, "%s %s %s", lstr, op, rstr); +	free(lstr); +	free(rstr); + +	return str; +} + +static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) +{ +	char *lstr; +	char *rstr; +	char *str = NULL; +	char *op = NULL; +	int len; + +	lstr = arg_to_str(filter, arg->num.left); +	rstr = arg_to_str(filter, arg->num.right); + +	switch (arg->num.type) { +	case FILTER_CMP_EQ: +		op = "=="; +		/* fall through */ +	case FILTER_CMP_NE: +		if (!op) +			op = "!="; +		/* fall through */ +	case FILTER_CMP_GT: +		if (!op) +			op = ">"; +		/* fall through */ +	case FILTER_CMP_LT: +		if (!op) +			op = "<"; +		/* fall through */ +	case FILTER_CMP_GE: +		if (!op) +			op = ">="; +		/* fall through */ +	case FILTER_CMP_LE: +		if (!op) +			op = "<="; + +		len = strlen(lstr) + strlen(op) + strlen(rstr) + 4; +		str = malloc_or_die(len); +		sprintf(str, "%s %s %s", lstr, op, rstr); + +		break; + +	default: +		/* ?? */ +		break; +	} + +	free(lstr); +	free(rstr); +	return str; +} + +static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) +{ +	char *str = NULL; +	char *op = NULL; +	int len; + +	switch (arg->str.type) { +	case FILTER_CMP_MATCH: +		op = "=="; +		/* fall through */ +	case FILTER_CMP_NOT_MATCH: +		if (!op) +			op = "!="; +		/* fall through */ +	case FILTER_CMP_REGEX: +		if (!op) +			op = "=~"; +		/* fall through */ +	case FILTER_CMP_NOT_REGEX: +		if (!op) +			op = "!~"; + +		len = strlen(arg->str.field->name) + strlen(op) + +			strlen(arg->str.val) + 6; +		str = malloc_or_die(len); +		snprintf(str, len, "%s %s \"%s\"", +			 arg->str.field->name, +			 op, arg->str.val); +		break; + +	default: +		/* ?? */ +		break; +	} +	return str; +} + +static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) +{ +	char *str; + +	switch (arg->type) { +	case FILTER_ARG_BOOLEAN: +		str = malloc_or_die(6); +		if (arg->boolean.value) +			strcpy(str, "TRUE"); +		else +			strcpy(str, "FALSE"); +		return str; + +	case FILTER_ARG_OP: +		return op_to_str(filter, arg); + +	case FILTER_ARG_NUM: +		return num_to_str(filter, arg); + +	case FILTER_ARG_STR: +		return str_to_str(filter, arg); + +	case FILTER_ARG_VALUE: +		return val_to_str(filter, arg); + +	case FILTER_ARG_FIELD: +		return field_to_str(filter, arg); + +	case FILTER_ARG_EXP: +		return exp_to_str(filter, arg); + +	default: +		/* ?? */ +		return NULL; +	} + +} + +/** + * pevent_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. + */ +char * +pevent_filter_make_string(struct event_filter *filter, int event_id) +{ +	struct 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); +} + +/** + * pevent_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 pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2) +{ +	struct filter_type *filter_type1; +	struct 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; +		switch (filter_type1->filter->type) { +		case FILTER_TRIVIAL_FALSE: +		case FILTER_TRIVIAL_TRUE: +			/* trivial types just need the type compared */ +			continue; +		default: +			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); +		result = strcmp(str1, str2) != 0; +		free(str1); +		free(str2); +		if (result) +			break; +	} + +	if (i < filter1->filters) +		return 0; +	return 1; +} + | 
