2446 lines · 51.9 KB · C
RawDownload
/* * 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,  see <http://www.gnu.org/licenses> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */#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"#define CPU "CPU" static struct format_field comm = {	.name = "COMM",}; static struct format_field cpu = {	.name = "CPU",}; struct event_list {	struct event_list	*next;	struct event_format	*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 = pevent_get_input_buf();	index = pevent_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, PEVENT_FILTER_ERROR_BUFSZ - len, fmt, ap);	va_end(ap);} 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);		if (*tok == NULL) {			free_token(token);			return EVENT_ERROR;		}		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; 	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 = 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(sizeof(*filter));	if (filter == NULL)		return NULL; 	memset(filter, 0, sizeof(*filter));	filter->pevent = pevent;	pevent_ref(pevent); 	return filter;} static struct filter_arg *allocate_arg(void){	return calloc(1, sizeof(struct filter_arg));} static void free_arg(struct filter_arg *arg){	if (!arg)		return; 	switch (arg->type) {	case FILTER_ARG_NONE:	case FILTER_ARG_BOOLEAN:		break; 	case FILTER_ARG_NUM:		free_arg(arg->num.left);		free_arg(arg->num.right);		break; 	case FILTER_ARG_EXP:		free_arg(arg->exp.left);		free_arg(arg->exp.right);		break; 	case FILTER_ARG_STR:		free(arg->str.val);		regfree(&arg->str.reg);		free(arg->str.buffer);		break; 	case FILTER_ARG_VALUE:		if (arg->value.type == FILTER_STRING ||		    arg->value.type == FILTER_CHAR)			free(arg->value.str);		break; 	case 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 event_format *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 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 enum pevent_errnofind_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;	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;	} 	reg = malloc(strlen(event_name) + 3);	if (reg == NULL)		return PEVENT_ERRNO__MEM_ALLOC_FAILED; 	sprintf(reg, "^%s$", event_name); 	ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB);	free(reg); 	if (ret)		return PEVENT_ERRNO__INVALID_EVENT_NAME; 	if (sys_name) {		reg = malloc(strlen(sys_name) + 3);		if (reg == NULL) {			regfree(&ereg);			return PEVENT_ERRNO__MEM_ALLOC_FAILED;		} 		sprintf(reg, "^%s$", sys_name);		ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB);		free(reg);		if (ret) {			regfree(&ereg);			return PEVENT_ERRNO__INVALID_EVENT_NAME;		}	} 	for (i = 0; i < pevent->nr_events; i++) {		event = pevent->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 PEVENT_ERRNO__EVENT_NOT_FOUND;	if (fail)		return PEVENT_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 pevent_errnocreate_arg_item(struct event_format *event, const char *token,		enum event_type type, struct filter_arg **parg, char *error_str){	struct format_field *field;	struct filter_arg *arg; 	arg = allocate_arg();	if (arg == NULL) {		show_error(error_str, "failed to allocate filter arg");		return PEVENT_ERRNO__MEM_ALLOC_FAILED;	} 	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) {			free_arg(arg);			show_error(error_str, "failed to allocate string filter arg");			return PEVENT_ERRNO__MEM_ALLOC_FAILED;		}		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 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 = FILTER_ARG_BOOLEAN;				arg->boolean.value = FILTER_FALSE;				break;			}		}		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 PEVENT_ERRNO__UNEXPECTED_TYPE;	}	*parg = arg;	return 0;} static struct filter_arg *create_arg_op(enum filter_op_type btype){	struct filter_arg *arg; 	arg = allocate_arg();	if (!arg)		return NULL; 	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();	if (!arg)		return NULL; 	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();	if (!arg)		return NULL; 	/* Use NUM and change if necessary */	arg->type = FILTER_ARG_NUM;	arg->op.type = etype; 	return arg;} static enum pevent_errnoadd_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 PEVENT_ERRNO__ILLEGAL_RVALUE;		} 		/*		 * Depending on the type, we may need to		 * convert this to a string or regex.		 */		switch (arg->value.type) {

Lines 1–500 of 2446.