1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446 |
- // 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-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 = 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 event_type read_token(char **tok)
- {
- enum event_type type;
- char *token = NULL;
- do {
- free_token(token);
- type = tep_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) &&
- tep_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 */
- tep_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 = tep_find_event(filter->pevent, id);
- filter_type->filter = NULL;
- filter->filters++;
- return filter_type;
- }
- /**
- * tep_filter_alloc - create a new event filter
- * @pevent: The pevent that this filter is associated with
- */
- struct event_filter *tep_filter_alloc(struct tep_handle *pevent)
- {
- struct event_filter *filter;
- filter = malloc(sizeof(*filter));
- if (filter == NULL)
- return NULL;
- memset(filter, 0, sizeof(*filter));
- filter->pevent = pevent;
- tep_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 tep_errno
- find_event(struct tep_handle *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;
- }
- 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 < 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 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 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 TEP_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 TEP_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 = 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 = 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 TEP_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->exp.type = etype;
- return arg;
- }
- static struct filter_arg *
- create_arg_cmp(enum filter_cmp_type ctype)
- {
- struct filter_arg *arg;
- arg = allocate_arg();
- if (!arg)
- return NULL;
- /* Use NUM and change if necessary */
- arg->type = FILTER_ARG_NUM;
- arg->num.type = ctype;
- return arg;
- }
- static enum tep_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 TEP_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 TEP_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 TEP_ERRNO__INVALID_REGEX;
- }
- break;
- default:
- show_error(error_str,
- "Illegal comparison for string");
- return TEP_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 TEP_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 TEP_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 TEP_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 TEP_ERRNO__SYNTAX_ERROR;
- }
- 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 enum tep_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 TEP_ERRNO__INVALID_ARG_TYPE;
- op->num.left = arg;
- break;
- default:
- return TEP_ERRNO__INVALID_ARG_TYPE;
- }
- 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,
- };
- static enum tep_errno
- reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child,
- struct filter_arg *arg, char *error_str)
- {
- struct filter_arg *other_child;
- struct filter_arg **ptr;
- if (parent->type != FILTER_ARG_OP &&
- arg->type != FILTER_ARG_OP) {
- show_error(error_str, "can not reparent other than OP");
- return TEP_ERRNO__REPARENT_NOT_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 {
- show_error(error_str, "Error in reparent op, find other child");
- return TEP_ERRNO__REPARENT_FAILED;
- }
- /* 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 0;
- }
- if (parent->op.right == old_child)
- ptr = &parent->op.right;
- else if (parent->op.left == old_child)
- ptr = &parent->op.left;
- else {
- show_error(error_str, "Error in reparent op");
- return TEP_ERRNO__REPARENT_FAILED;
- }
- *ptr = arg;
- free_arg(old_child);
- return 0;
- }
- /* Returns either filter_vals (success) or tep_errno (failfure) */
- static int test_arg(struct filter_arg *parent, struct filter_arg *arg,
- char *error_str)
- {
- int 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, error_str);
- if (lval != FILTER_VAL_NORM)
- return lval;
- rval = test_arg(arg, arg->exp.right, error_str);
- if (rval != FILTER_VAL_NORM)
- return rval;
- return FILTER_VAL_NORM;
- case FILTER_ARG_NUM:
- lval = test_arg(arg, arg->num.left, error_str);
- if (lval != FILTER_VAL_NORM)
- return lval;
- rval = test_arg(arg, arg->num.right, error_str);
- 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, error_str);
- 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, error_str);
- if (rval != FILTER_VAL_NORM)
- return rval;
- return reparent_op_arg(parent, arg, arg->op.right,
- error_str);
- case FILTER_VAL_FALSE:
- if (arg->op.type == FILTER_OP_AND)
- return FILTER_VAL_FALSE;
- rval = test_arg(arg, arg->op.right, error_str);
- if (rval != FILTER_VAL_NORM)
- return rval;
- return reparent_op_arg(parent, arg, arg->op.right,
- error_str);
- default:
- return lval;
- }
- }
- rval = test_arg(arg, arg->op.right, error_str);
- switch (rval) {
- case FILTER_VAL_NORM:
- default:
- 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;
- return reparent_op_arg(parent, arg, arg->op.left,
- error_str);
- 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;
- return reparent_op_arg(parent, arg, arg->op.left,
- error_str);
- }
- return rval;
- default:
- show_error(error_str, "bad arg in filter tree");
- return TEP_ERRNO__BAD_FILTER_ARG;
- }
- return FILTER_VAL_NORM;
- }
- /* Remove any unknown event fields */
- static int collapse_tree(struct filter_arg *arg,
- struct filter_arg **arg_collapsed, char *error_str)
- {
- int ret;
- ret = test_arg(arg, arg, error_str);
- switch (ret) {
- case FILTER_VAL_NORM:
- break;
- case FILTER_VAL_TRUE:
- case FILTER_VAL_FALSE:
- free_arg(arg);
- arg = allocate_arg();
- if (arg) {
- arg->type = FILTER_ARG_BOOLEAN;
- arg->boolean.value = ret == FILTER_VAL_TRUE;
- } else {
- show_error(error_str, "Failed to allocate filter arg");
- ret = TEP_ERRNO__MEM_ALLOC_FAILED;
- }
- break;
- default:
- /* test_arg() already set the error_str */
- free_arg(arg);
- arg = NULL;
- break;
- }
- *arg_collapsed = arg;
- return ret;
- }
- static enum tep_errno
- 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;
- enum tep_errno ret;
- *parg = NULL;
- do {
- free(token);
- type = read_token(&token);
- switch (type) {
- case EVENT_SQUOTE:
- case EVENT_DQUOTE:
- case EVENT_ITEM:
- ret = create_arg_item(event, token, type, &arg, error_str);
- if (ret < 0)
- 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_syntax;
- free(token);
- *parg = current_exp;
- return 0;
- }
- } else
- goto fail_syntax;
- arg = NULL;
- break;
- case EVENT_DELIM:
- if (*token == ',') {
- show_error(error_str, "Illegal token ','");
- ret = TEP_ERRNO__ILLEGAL_TOKEN;
- goto fail;
- }
- if (*token == '(') {
- if (left_item) {
- show_error(error_str,
- "Open paren can not come after item");
- ret = TEP_ERRNO__INVALID_PAREN;
- goto fail;
- }
- if (current_exp) {
- show_error(error_str,
- "Open paren can not come after expression");
- ret = TEP_ERRNO__INVALID_PAREN;
- goto fail;
- }
- ret = process_filter(event, &arg, error_str, 0);
- if (ret != TEP_ERRNO__UNBALANCED_PAREN) {
- if (ret == 0) {
- show_error(error_str,
- "Unbalanced number of '('");
- ret = TEP_ERRNO__UNBALANCED_PAREN;
- }
- goto fail;
- }
- ret = 0;
- /* A not wants just one expression */
- if (not) {
- if (current_op)
- goto fail_syntax;
- *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_syntax;
- /* Make sure everything is finished at this level */
- if (current_exp && !check_op_done(current_exp))
- goto fail_syntax;
- if (current_op && !check_op_done(current_op))
- goto fail_syntax;
- if (current_op)
- *parg = current_op;
- else
- *parg = current_exp;
- free(token);
- return TEP_ERRNO__UNBALANCED_PAREN;
- }
- 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_syntax;
- /* fall through */
- case OP_NOT:
- /* logic only processes ops and exp */
- if (left_item)
- goto fail_syntax;
- break;
- case OP_EXP:
- case OP_CMP:
- if (!left_item)
- goto fail_syntax;
- break;
- case OP_NONE:
- show_error(error_str,
- "Unknown op token %s", token);
- ret = TEP_ERRNO__UNKNOWN_TOKEN;
- goto fail;
- }
- ret = 0;
- switch (op_type) {
- case OP_BOOL:
- arg = create_arg_op(btype);
- if (arg == NULL)
- goto fail_alloc;
- 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 (arg == NULL)
- goto fail_alloc;
- 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 (arg == NULL)
- goto fail_alloc;
- 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_syntax;
- }
- current_exp = arg;
- break;
- default:
- break;
- }
- arg = NULL;
- if (ret < 0)
- goto fail_syntax;
- break;
- case EVENT_NONE:
- break;
- case EVENT_ERROR:
- goto fail_alloc;
- default:
- goto fail_syntax;
- }
- } while (type != EVENT_NONE);
- if (!current_op && !current_exp)
- goto fail_syntax;
- if (!current_op)
- current_op = current_exp;
- ret = collapse_tree(current_op, parg, error_str);
- /* collapse_tree() may free current_op, and updates parg accordingly */
- current_op = NULL;
- if (ret < 0)
- goto fail;
- free(token);
- return 0;
- fail_alloc:
- show_error(error_str, "failed to allocate filter arg");
- ret = TEP_ERRNO__MEM_ALLOC_FAILED;
- goto fail;
- fail_syntax:
- show_error(error_str, "Syntax error");
- ret = TEP_ERRNO__SYNTAX_ERROR;
- fail:
- free_arg(current_op);
- free_arg(current_exp);
- free_arg(arg);
- free(token);
- return ret;
- }
- static enum tep_errno
- process_event(struct event_format *event, const char *filter_str,
- struct filter_arg **parg, char *error_str)
- {
- int ret;
- tep_buffer_init(filter_str, strlen(filter_str));
- ret = process_filter(event, parg, error_str, 0);
- if (ret < 0)
- return ret;
- /* If parg is NULL, then make it into FALSE */
- if (!*parg) {
- *parg = allocate_arg();
- if (*parg == NULL)
- return TEP_ERRNO__MEM_ALLOC_FAILED;
- (*parg)->type = FILTER_ARG_BOOLEAN;
- (*parg)->boolean.value = FILTER_FALSE;
- }
- return 0;
- }
- static enum tep_errno
- 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;
- enum tep_errno 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();
- if (arg == NULL)
- return TEP_ERRNO__MEM_ALLOC_FAILED;
- arg->type = FILTER_ARG_BOOLEAN;
- arg->boolean.value = FILTER_TRUE;
- }
- filter_type = add_filter_type(filter, event->id);
- if (filter_type == NULL) {
- free_arg(arg);
- return TEP_ERRNO__MEM_ALLOC_FAILED;
- }
- if (filter_type->filter)
- free_arg(filter_type->filter);
- filter_type->filter = arg;
- return 0;
- }
- static void filter_init_error_buf(struct event_filter *filter)
- {
- /* clear buffer to reset show error */
- tep_buffer_init("", 0);
- filter->error_buffer[0] = '\0';
- }
- /**
- * tep_filter_add_filter_str - add a new filter
- * @filter: the event filter to add to
- * @filter_str: the filter string that contains the filter
- *
- * Returns 0 if the filter was successfully added or a
- * negative error code. Use tep_filter_strerror() to see
- * actual error message in case of error.
- */
- enum tep_errno tep_filter_add_filter_str(struct event_filter *filter,
- const char *filter_str)
- {
- struct tep_handle *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;
- enum tep_errno rtn = 0; /* TEP_ERRNO__SUCCESS */
- int len;
- int ret;
- filter_init_error_buf(filter);
- 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(len + 1);
- if (this_event == NULL) {
- /* This can only happen when events is NULL, but still */
- free_events(events);
- return TEP_ERRNO__MEM_ALLOC_FAILED;
- }
- 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) {
- /* This can only happen when events is NULL, but still */
- free_events(events);
- free(this_event);
- return TEP_ERRNO__FILTER_NOT_FOUND;
- }
- /* Find this event */
- ret = find_event(pevent, &events, strim(sys_name), strim(event_name));
- if (ret < 0) {
- free_events(events);
- free(this_event);
- return ret;
- }
- 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,
- filter->error_buffer);
- /* Failures are returned if a parse error happened */
- if (ret < 0)
- rtn = ret;
- if (ret >= 0 && pevent->test_filters) {
- char *test;
- test = tep_filter_make_string(filter, event->event->id);
- if (test) {
- 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);
- }
- /**
- * tep_filter_strerror - fill error message in a buffer
- * @filter: the event filter contains error
- * @err: the error code
- * @buf: the buffer to be filled in
- * @buflen: the size of the buffer
- *
- * Returns 0 if message was filled successfully, -1 if error
- */
- int tep_filter_strerror(struct event_filter *filter, enum tep_errno err,
- char *buf, size_t buflen)
- {
- if (err <= __TEP_ERRNO__START || err >= __TEP_ERRNO__END)
- return -1;
- if (strlen(filter->error_buffer) > 0) {
- size_t len = snprintf(buf, buflen, "%s", filter->error_buffer);
- if (len > buflen)
- return -1;
- return 0;
- }
- return tep_strerror(filter->pevent, err, buf, buflen);
- }
- /**
- * tep_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 tep_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;
- }
- /**
- * tep_filter_reset - clear all filters in a filter
- * @filter: the event filter to reset
- *
- * Removes all filters from a filter and resets it.
- */
- void tep_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 tep_filter_free(struct event_filter *filter)
- {
- tep_unref(filter->pevent);
- tep_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 = tep_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();
- if (arg == NULL) {
- free(str);
- return -1;
- }
- 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);
- if (filter_type == NULL) {
- free(str);
- free_arg(arg);
- return -1;
- }
- filter_type->filter = arg;
- free(str);
- return 0;
- }
- filter_event(filter, event, str, NULL);
- free(str);
- return 0;
- }
- /**
- * tep_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 tep_filter_copy(struct event_filter *dest, struct event_filter *source)
- {
- int ret = 0;
- int i;
- tep_filter_reset(dest);
- for (i = 0; i < source->filters; i++) {
- if (copy_filter_type(dest, source, &source->event_filters[i]))
- ret = -1;
- }
- return ret;
- }
- /**
- * tep_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 tep_update_trivial(struct event_filter *dest, struct event_filter *source,
- enum filter_trivial_type type)
- {
- struct tep_handle *src_pevent;
- struct tep_handle *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 = tep_find_event_by_name(src_pevent,
- event->system,
- event->name);
- if (!event)
- return -1;
- }
- str = tep_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;
- }
- /**
- * tep_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.
- *
- * Returns 0 on success and -1 if there was a problem.
- */
- int tep_filter_clear_trivial(struct event_filter *filter,
- enum filter_trivial_type type)
- {
- struct filter_type *filter_type;
- int count = 0;
- int *ids = NULL;
- int i;
- if (!filter->filters)
- return 0;
- /*
- * Two steps, first get all ids with trivial filters.
- * then remove those ids.
- */
- for (i = 0; i < filter->filters; i++) {
- int *new_ids;
- 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;
- break;
- case FILTER_TRIVIAL_TRUE:
- if (!filter_type->filter->boolean.value)
- continue;
- default:
- break;
- }
- new_ids = realloc(ids, sizeof(*ids) * (count + 1));
- if (!new_ids) {
- free(ids);
- return -1;
- }
- ids = new_ids;
- ids[count++] = filter_type->event_id;
- }
- if (!count)
- return 0;
- for (i = 0; i < count; i++)
- tep_filter_remove_event(filter, ids[i]);
- free(ids);
- return 0;
- }
- /**
- * tep_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 tep_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 tep_record *record, enum tep_errno *err);
- static const char *
- get_comm(struct event_format *event, struct tep_record *record)
- {
- const char *comm;
- int pid;
- pid = tep_data_pid(event->pevent, record);
- comm = tep_data_comm_from_pid(event->pevent, pid);
- return comm;
- }
- static unsigned long long
- get_value(struct event_format *event,
- struct format_field *field, struct tep_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;
- }
- /* Handle our dummy "cpu" field */
- if (field == &cpu)
- return record->cpu;
- tep_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 tep_record *record, enum tep_errno *err);
- static unsigned long long
- get_exp_value(struct event_format *event, struct filter_arg *arg,
- struct tep_record *record, enum tep_errno *err)
- {
- unsigned long long lval, rval;
- lval = get_arg_value(event, arg->exp.left, record, err);
- rval = get_arg_value(event, arg->exp.right, record, err);
- if (*err) {
- /*
- * There was an error, no need to process anymore.
- */
- return 0;
- }
- 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:
- if (!*err)
- *err = TEP_ERRNO__INVALID_EXP_TYPE;
- }
- return 0;
- }
- static unsigned long long
- get_arg_value(struct event_format *event, struct filter_arg *arg,
- struct tep_record *record, enum tep_errno *err)
- {
- 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) {
- if (!*err)
- *err = TEP_ERRNO__NOT_A_NUMBER;
- }
- return arg->value.val;
- case FILTER_ARG_EXP:
- return get_exp_value(event, arg, record, err);
- default:
- if (!*err)
- *err = TEP_ERRNO__INVALID_ARG_TYPE;
- }
- return 0;
- }
- static int test_num(struct event_format *event, struct filter_arg *arg,
- struct tep_record *record, enum tep_errno *err)
- {
- unsigned long long lval, rval;
- lval = get_arg_value(event, arg->num.left, record, err);
- rval = get_arg_value(event, arg->num.right, record, err);
- if (*err) {
- /*
- * There was an error, no need to process anymore.
- */
- return 0;
- }
- 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:
- if (!*err)
- *err = TEP_ERRNO__ILLEGAL_INTEGER_CMP;
- return 0;
- }
- }
- static const char *get_field_str(struct filter_arg *arg, struct tep_record *record)
- {
- struct event_format *event;
- struct tep_handle *pevent;
- unsigned long long addr;
- const char *val = NULL;
- unsigned int size;
- char hex[64];
- /* If the field is not a string convert it */
- if (arg->str.field->flags & FIELD_IS_STRING) {
- val = record->data + arg->str.field->offset;
- size = arg->str.field->size;
- if (arg->str.field->flags & FIELD_IS_DYNAMIC) {
- addr = *(unsigned int *)val;
- val = record->data + (addr & 0xffff);
- size = addr >> 16;
- }
- /*
- * We need to copy the data since we can't be sure the field
- * is null terminated.
- */
- if (*(val + size - 1)) {
- /* copy it */
- memcpy(arg->str.buffer, val, arg->str.field->size);
- /* the buffer is already NULL terminated */
- val = arg->str.buffer;
- }
- } else {
- event = arg->str.field->event;
- pevent = event->pevent;
- addr = get_value(event, arg->str.field, record);
- if (arg->str.field->flags & (FIELD_IS_POINTER | FIELD_IS_LONG))
- /* convert to a kernel symbol */
- val = tep_find_function(pevent, addr);
- if (val == NULL) {
- /* just use the hex of the string name */
- snprintf(hex, 64, "0x%llx", addr);
- val = hex;
- }
- }
- return val;
- }
- static int test_str(struct event_format *event, struct filter_arg *arg,
- struct tep_record *record, enum tep_errno *err)
- {
- 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:
- if (!*err)
- *err = TEP_ERRNO__ILLEGAL_STRING_CMP;
- return 0;
- }
- }
- static int test_op(struct event_format *event, struct filter_arg *arg,
- struct tep_record *record, enum tep_errno *err)
- {
- switch (arg->op.type) {
- case FILTER_OP_AND:
- return test_filter(event, arg->op.left, record, err) &&
- test_filter(event, arg->op.right, record, err);
- case FILTER_OP_OR:
- return test_filter(event, arg->op.left, record, err) ||
- test_filter(event, arg->op.right, record, err);
- case FILTER_OP_NOT:
- return !test_filter(event, arg->op.right, record, err);
- default:
- if (!*err)
- *err = TEP_ERRNO__INVALID_OP_TYPE;
- return 0;
- }
- }
- static int test_filter(struct event_format *event, struct filter_arg *arg,
- struct tep_record *record, enum tep_errno *err)
- {
- if (*err) {
- /*
- * There was an error, no need to process anymore.
- */
- return 0;
- }
- switch (arg->type) {
- case FILTER_ARG_BOOLEAN:
- /* easy case */
- return arg->boolean.value;
- case FILTER_ARG_OP:
- return test_op(event, arg, record, err);
- case FILTER_ARG_NUM:
- return test_num(event, arg, record, err);
- case FILTER_ARG_STR:
- return test_str(event, arg, record, err);
- 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, err);
- default:
- if (!*err)
- *err = TEP_ERRNO__INVALID_ARG_TYPE;
- return 0;
- }
- }
- /**
- * tep_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 tep_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;
- }
- /**
- * tep_filter_match - test if a record matches a filter
- * @filter: filter struct with filter information
- * @record: the record to test against the filter
- *
- * Returns: match result or error code (prefixed with TEP_ERRNO__)
- * FILTER_MATCH - filter found for event and @record matches
- * FILTER_MISS - filter found for event and @record does not match
- * FILTER_NOT_FOUND - no filter found for @record's event
- * NO_FILTER - if no filters exist
- * otherwise - error occurred during test
- */
- enum tep_errno tep_filter_match(struct event_filter *filter,
- struct tep_record *record)
- {
- struct tep_handle *pevent = filter->pevent;
- struct filter_type *filter_type;
- int event_id;
- int ret;
- enum tep_errno err = 0;
- filter_init_error_buf(filter);
- if (!filter->filters)
- return TEP_ERRNO__NO_FILTER;
- event_id = tep_data_type(pevent, record);
- filter_type = find_filter_type(filter, event_id);
- if (!filter_type)
- return TEP_ERRNO__FILTER_NOT_FOUND;
- ret = test_filter(filter_type->event, filter_type->filter, record, &err);
- if (err)
- return err;
- return ret ? TEP_ERRNO__FILTER_MATCH : TEP_ERRNO__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;
- 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;
- }
- asprintf(&str, val ? "TRUE" : "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;
- }
- asprintf(&str, "(%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 */
- asprintf(&str, right_val ? "FALSE" : "TRUE");
- break;
- }
- asprintf(&str, "%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 = NULL;
- asprintf(&str, "%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 = NULL;
- lstr = arg_to_str(filter, arg->exp.left);
- rstr = arg_to_str(filter, arg->exp.right);
- if (!lstr || !rstr)
- goto out;
- 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:
- op = "[ERROR IN EXPRESSION TYPE]";
- break;
- }
- asprintf(&str, "%s %s %s", lstr, op, rstr);
- out:
- 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;
- lstr = arg_to_str(filter, arg->num.left);
- rstr = arg_to_str(filter, arg->num.right);
- if (!lstr || !rstr)
- goto out;
- 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 = "<=";
- asprintf(&str, "%s %s %s", lstr, op, rstr);
- break;
- default:
- /* ?? */
- break;
- }
- out:
- 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;
- 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 = "!~";
- asprintf(&str, "%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 = NULL;
- switch (arg->type) {
- case FILTER_ARG_BOOLEAN:
- asprintf(&str, arg->boolean.value ? "TRUE" : "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;
- }
- }
- /**
- * tep_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 or allocation failed.
- */
- char *
- tep_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);
- }
- /**
- * tep_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 tep_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);
- if (str1 && str2)
- result = strcmp(str1, str2) != 0;
- else
- /* bail out if allocation fails */
- result = 1;
- free(str1);
- free(str2);
- if (result)
- break;
- }
- if (i < filter1->filters)
- return 0;
- return 1;
- }
|