cregit-Linux how code gets into the kernel

Release 4.10 tools/lib/traceevent/parse-filter.c

/*
 * 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); }

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt14492.31%133.33%
namhyung kimnamhyung kim127.69%266.67%
Total156100.00%3100.00%


static void free_token(char *token) { pevent_free_token(token); }

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt15100.00%1100.00%
Total15100.00%1100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt12887.67%150.00%
namhyung kimnamhyung kim1812.33%150.00%
Total146100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt62100.00%1100.00%
Total62100.00%1100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt60100.00%1100.00%
Total60100.00%1100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt19095.96%150.00%
namhyung kimnamhyung kim84.04%150.00%
Total198100.00%2100.00%

/** * 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; }

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt5484.38%150.00%
namhyung kimnamhyung kim1015.62%150.00%
Total64100.00%2100.00%


static struct filter_arg *allocate_arg(void) { return calloc(1, sizeof(struct filter_arg)); }

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt1777.27%150.00%
namhyung kimnamhyung kim522.73%150.00%
Total22100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt167100.00%2100.00%
Total167100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt4976.56%150.00%
namhyung kimnamhyung kim1523.44%150.00%
Total64100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt91100.00%1100.00%
Total91100.00%1100.00%


static enum pevent_errno 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; 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; }

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt25281.82%133.33%
namhyung kimnamhyung kim5618.18%266.67%
Total308100.00%3100.00%


static void free_events(struct event_list *events) { struct event_list *event; while (events) { event = events; events = events->next; free(event); } }

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt37100.00%1100.00%
Total37100.00%1100.00%


static enum pevent_errno create_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; }

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt24783.73%342.86%
namhyung kimnamhyung kim4816.27%457.14%
Total295100.00%7100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt3982.98%150.00%
namhyung kimnamhyung kim817.02%150.00%
Total47100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt3982.98%150.00%
namhyung kimnamhyung kim817.02%150.00%
Total47100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt4083.33%150.00%
namhyung kimnamhyung kim816.67%150.00%
Total48100.00%2100.00%


static enum pevent_errno 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 PEVENT_ERRNO__ILLEGAL_RVALUE; } /* * 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 PEVENT_ERRNO__ILLEGAL_LVALUE; } /* 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 PEVENT_ERRNO__INVALID_REGEX; } break; default: show_error(error_str, "Illegal comparison for string"); return PEVENT_ERRNO__ILLEGAL_STRING_CMP; } 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) { show_error(error_str, "Failed to allocate string filter"); return PEVENT_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 PEVENT_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 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 PEVENT_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 PEVENT_ERRNO__SYNTAX_ERROR; }

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt52293.05%266.67%
namhyung kimnamhyung kim396.95%133.33%
Total561100.00%3100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt42100.00%1100.00%
Total42100.00%1100.00%


static enum pevent_errno 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 PEVENT_ERRNO__INVALID_ARG_TYPE; op->num.left = arg; break; default: return PEVENT_ERRNO__INVALID_ARG_TYPE; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
steven rostedtsteven rostedt11696.67%150.00%
namhyung kimnamhyung kim43.33%150.00%
Total120100.00%2100.00%

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