// 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(®, "^%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_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 2274. Load more
// 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 */