2283 lines · 48.3 KB · C
RawDownload
// 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_errnofind_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(&reg, "^%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(&reg, "^%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_errnocreate_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_errnoadd_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 */

Lines 1–500 of 2283.