• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3  * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
4  *
5  * Updates:
6  * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
7  *
8  */
9 #include <trace-seq.h>
10 #include <stdlib.h>
11 #include <ctype.h>
12 #include <errno.h>
13 
14 #include "tracefs.h"
15 #include "tracefs-local.h"
16 
17 enum {
18 	S_START,
19 	S_COMPARE,
20 	S_NOT,
21 	S_CONJUNCTION,
22 	S_OPEN_PAREN,
23 	S_CLOSE_PAREN,
24 };
25 
26 static const struct tep_format_field common_timestamp = {
27 	.type			= "u64",
28 	.name			= "common_timestamp",
29 	.size			= 8,
30 };
31 
32 static const struct tep_format_field common_timestamp_usecs = {
33 	.type			= "u64",
34 	.name			= "common_timestamp.usecs",
35 	.size			= 8,
36 };
37 
38 static const struct tep_format_field common_comm = {
39 	.type			= "char *",
40 	.name			= "common_comm",
41 	.size			= 16,
42 };
43 
44 /*
45  * This also must be able to accept fields that are OK via the histograms,
46  * such as common_timestamp.
47  */
get_event_field(struct tep_event * event,const char * field_name)48 static const struct tep_format_field *get_event_field(struct tep_event *event,
49 					 const char *field_name)
50 {
51 	const struct tep_format_field *field;
52 
53 	if (!strcmp(field_name, TRACEFS_TIMESTAMP))
54 		return &common_timestamp;
55 
56 	if (!strcmp(field_name, TRACEFS_TIMESTAMP_USECS))
57 		return &common_timestamp_usecs;
58 
59 	field = tep_find_any_field(event, field_name);
60 	if (!field && (!strcmp(field_name, "COMM") || !strcmp(field_name, "comm")))
61 		return &common_comm;
62 
63 	return field;
64 }
65 
66 __hidden bool
trace_verify_event_field(struct tep_event * event,const char * field_name,const struct tep_format_field ** ptr_field)67 trace_verify_event_field(struct tep_event *event,
68 			 const char *field_name,
69 			 const struct tep_format_field **ptr_field)
70 {
71 	const struct tep_format_field *field;
72 
73 	field = get_event_field(event, field_name);
74 	if (!field) {
75 		errno = ENODEV;
76 		return false;
77 	}
78 
79 	if (ptr_field)
80 		*ptr_field = field;
81 
82 	return true;
83 }
84 
trace_test_state(int state)85 __hidden int trace_test_state(int state)
86 {
87 	switch (state) {
88 	case S_START:
89 	case S_CLOSE_PAREN:
90 	case S_COMPARE:
91 		return 0;
92 	}
93 
94 	errno = EBADE;
95 	return -1;
96 }
97 
append_filter(char ** filter,unsigned int * state,unsigned int * open_parens,struct tep_event * event,enum tracefs_filter type,const char * field_name,enum tracefs_compare compare,const char * val)98 static int append_filter(char **filter, unsigned int *state,
99 			 unsigned int *open_parens,
100 			 struct tep_event *event,
101 			 enum tracefs_filter type,
102 			 const char *field_name,
103 			 enum tracefs_compare compare,
104 			 const char *val)
105 {
106 	const struct tep_format_field *field;
107 	bool is_string;
108 	char *conj = "||";
109 	char *tmp;
110 
111 	switch (type) {
112 	case TRACEFS_FILTER_COMPARE:
113 		switch (*state) {
114 		case S_START:
115 		case S_OPEN_PAREN:
116 		case S_CONJUNCTION:
117 		case S_NOT:
118 			break;
119 		default:
120 			goto inval;
121 		}
122 		break;
123 
124 	case TRACEFS_FILTER_AND:
125 		conj = "&&";
126 		/* Fall through */
127 	case TRACEFS_FILTER_OR:
128 		switch (*state) {
129 		case S_COMPARE:
130 		case S_CLOSE_PAREN:
131 			break;
132 		default:
133 			goto inval;
134 		}
135 		/* Don't lose old filter on failure */
136 		tmp = strdup(*filter);
137 		if (!tmp)
138 			return -1;
139 		tmp = append_string(tmp, NULL, conj);
140 		if (!tmp)
141 			return -1;
142 		free(*filter);
143 		*filter = tmp;
144 		*state = S_CONJUNCTION;
145 		return 0;
146 
147 	case TRACEFS_FILTER_NOT:
148 		switch (*state) {
149 		case S_START:
150 		case S_OPEN_PAREN:
151 		case S_CONJUNCTION:
152 		case S_NOT:
153 			break;
154 		default:
155 			goto inval;
156 		}
157 		if (*filter) {
158 			tmp = strdup(*filter);
159 			tmp = append_string(tmp, NULL, "!");
160 		} else {
161 			tmp = strdup("!");
162 		}
163 		if (!tmp)
164 			return -1;
165 		free(*filter);
166 		*filter = tmp;
167 		*state = S_NOT;
168 		return 0;
169 
170 	case TRACEFS_FILTER_OPEN_PAREN:
171 		switch (*state) {
172 		case S_START:
173 		case S_OPEN_PAREN:
174 		case S_NOT:
175 		case S_CONJUNCTION:
176 			break;
177 		default:
178 			goto inval;
179 		}
180 		if (*filter) {
181 			tmp = strdup(*filter);
182 			tmp = append_string(tmp, NULL, "(");
183 		} else {
184 			tmp = strdup("(");
185 		}
186 		if (!tmp)
187 			return -1;
188 		free(*filter);
189 		*filter = tmp;
190 		*state = S_OPEN_PAREN;
191 		(*open_parens)++;
192 		return 0;
193 
194 	case TRACEFS_FILTER_CLOSE_PAREN:
195 		switch (*state) {
196 		case S_CLOSE_PAREN:
197 		case S_COMPARE:
198 			break;
199 		default:
200 			goto inval;
201 		}
202 		if (!*open_parens)
203 			goto inval;
204 
205 		tmp = strdup(*filter);
206 		if (!tmp)
207 			return -1;
208 		tmp = append_string(tmp, NULL, ")");
209 		if (!tmp)
210 			return -1;
211 		free(*filter);
212 		*filter = tmp;
213 		*state = S_CLOSE_PAREN;
214 		(*open_parens)--;
215 		return 0;
216 	}
217 
218 	if (!field_name || !val)
219 		goto inval;
220 
221 	if (!trace_verify_event_field(event, field_name, &field))
222 		return -1;
223 
224 	is_string = field->flags & TEP_FIELD_IS_STRING;
225 
226 	if (!is_string && (field->flags & TEP_FIELD_IS_ARRAY))
227 		goto inval;
228 
229 	if (*filter) {
230 		tmp = strdup(*filter);
231 		if (!tmp)
232 			return -1;
233 		tmp = append_string(tmp, NULL, field_name);
234 	} else {
235 		tmp = strdup(field_name);
236 	}
237 
238 	switch (compare) {
239 	case TRACEFS_COMPARE_EQ: tmp = append_string(tmp, NULL, " == "); break;
240 	case TRACEFS_COMPARE_NE: tmp = append_string(tmp, NULL, " != "); break;
241 	case TRACEFS_COMPARE_RE:
242 		if (!is_string)
243 			goto inval;
244 		tmp = append_string(tmp, NULL, "~");
245 		break;
246 	default:
247 		if (is_string)
248 			goto inval;
249 	}
250 
251 	switch (compare) {
252 	case TRACEFS_COMPARE_GT: tmp = append_string(tmp, NULL, " > "); break;
253 	case TRACEFS_COMPARE_GE: tmp = append_string(tmp, NULL, " >= "); break;
254 	case TRACEFS_COMPARE_LT: tmp = append_string(tmp, NULL, " < "); break;
255 	case TRACEFS_COMPARE_LE: tmp = append_string(tmp, NULL, " <= "); break;
256 	case TRACEFS_COMPARE_AND: tmp = append_string(tmp, NULL, " & "); break;
257 	default: break;
258 	}
259 
260 	tmp = append_string(tmp, NULL, val);
261 
262 	if (!tmp)
263 		return -1;
264 
265 	free(*filter);
266 	*filter = tmp;
267 	*state = S_COMPARE;
268 
269 	return 0;
270 inval:
271 	errno = EINVAL;
272 	return -1;
273 }
274 
count_parens(char * filter,unsigned int * state)275 static int count_parens(char *filter, unsigned int *state)
276 {
277 	bool backslash = false;
278 	int quote = 0;
279 	int open = 0;
280 	int i;
281 
282 	if (!filter)
283 		return 0;
284 
285 	for (i = 0; filter[i]; i++) {
286 		if (quote) {
287 			if (backslash)
288 				backslash = false;
289 			else if (filter[i] == '\\')
290 				backslash = true;
291 			else if (quote == filter[i])
292 				quote = 0;
293 			continue;
294 		}
295 
296 		switch (filter[i]) {
297 		case '(':
298 			*state = S_OPEN_PAREN;
299 			open++;
300 			break;
301 		case ')':
302 			*state = S_CLOSE_PAREN;
303 			open--;
304 			break;
305 		case '\'':
306 		case '"':
307 			*state = S_COMPARE;
308 			quote = filter[i];
309 			break;
310 		case '!':
311 			switch (filter[i+1]) {
312 			case '=':
313 			case '~':
314 				*state = S_COMPARE;
315 				i++;
316 				break;
317 			default:
318 				*state = S_NOT;
319 			}
320 			break;
321 		case '&':
322 		case '|':
323 			if (filter[i] == filter[i+1]) {
324 				*state = S_CONJUNCTION;
325 				i++;
326 				break;
327 			}
328 			/* Fall through */
329 		case '0' ... '9':
330 		case 'a' ... 'z':
331 		case 'A' ... 'Z':
332 		case '_': case '+': case '-': case '*': case '/':
333 			*state = S_COMPARE;
334 			break;
335 		}
336 	}
337 	return open;
338 }
339 
trace_append_filter(char ** filter,unsigned int * state,unsigned int * open_parens,struct tep_event * event,enum tracefs_filter type,const char * field_name,enum tracefs_compare compare,const char * val)340 __hidden int trace_append_filter(char **filter, unsigned int *state,
341 			 unsigned int *open_parens,
342 			 struct tep_event *event,
343 			 enum tracefs_filter type,
344 			 const char *field_name,
345 			 enum tracefs_compare compare,
346 			 const char *val)
347 {
348 	return append_filter(filter, state, open_parens, event, type,
349 			     field_name, compare, val);
350 }
351 
352 /**
353  * tracefs_filter_string_append - create or append a filter for an event
354  * @event: tep_event to create / append a filter for
355  * @filter: Pointer to string to append to (pointer to NULL to create)
356  * @type: The type of element to add to the filter
357  * @field: For @type == TRACEFS_FILTER_COMPARE, the field to compare
358  * @compare: For @type == TRACEFS_FILTER_COMPARE, how to compare @field to @val
359  * @val: For @type == TRACEFS_FILTER_COMPARE, what value @field is to be
360  *
361  * This will put together a filter string for the starting event
362  * of @synth. It check to make sure that what is added is correct compared
363  * to the filter that is already built.
364  *
365  * @type can be:
366  *     TRACEFS_FILTER_COMPARE:        See below
367  *     TRACEFS_FILTER_AND:            Append "&&" to the filter
368  *     TRACEFS_FILTER_OR:             Append "||" to the filter
369  *     TRACEFS_FILTER_NOT:            Append "!" to the filter
370  *     TRACEFS_FILTER_OPEN_PAREN:     Append "(" to the filter
371  *     TRACEFS_FILTER_CLOSE_PAREN:    Append ")" to the filter
372  *
373  * For all types except TRACEFS_FILTER_COMPARE, the @field, @compare,
374  * and @val are ignored.
375  *
376  * For @type == TRACEFS_FILTER_COMPARE.
377  *
378  *  @field is the name of the field for the start event to compare.
379  *         If it is not a field for the start event, this return an
380  *         error.
381  *
382  *  @compare can be one of:
383  *     TRACEFS_COMPARE_EQ:       Test @field == @val
384  *     TRACEFS_COMPARE_NE:       Test @field != @val
385  *     TRACEFS_COMPARE_GT:       Test @field > @val
386  *     TRACEFS_COMPARE_GE:       Test @field >= @val
387  *     TRACEFS_COMPARE_LT:       Test @field < @val
388  *     TRACEFS_COMPARE_LE:       Test @field <= @val
389  *     TRACEFS_COMPARE_RE:       Test @field ~ @val
390  *     TRACEFS_COMPARE_AND:      Test @field & @val
391  *
392  * If the @field is of type string, and @compare is not
393  *   TRACEFS_COMPARE_EQ, TRACEFS_COMPARE_NE or TRACEFS_COMPARE_RE,
394  *   then this will return an error.
395  *
396  * Various other checks are made, for instance, if more CLOSE_PARENs
397  * are added than existing OPEN_PARENs. Or if AND is added after an
398  * OPEN_PAREN or another AND or an OR or a NOT.
399  *
400  * Returns 0 on success and -1 on failure.
401  */
tracefs_filter_string_append(struct tep_event * event,char ** filter,enum tracefs_filter type,const char * field,enum tracefs_compare compare,const char * val)402 int tracefs_filter_string_append(struct tep_event *event, char **filter,
403 				 enum tracefs_filter type,
404 				 const char *field, enum tracefs_compare compare,
405 				 const char *val)
406 {
407 	unsigned int open_parens;
408 	unsigned int state = 0;
409 	char *str = NULL;
410 	int open;
411 	int ret;
412 
413 	if (!filter) {
414 		errno = EINVAL;
415 		return -1;
416 	}
417 
418 	open = count_parens(*filter, &state);
419 	if (open < 0) {
420 		errno = EINVAL;
421 		return -1;
422 	}
423 
424 	if (*filter) {
425 		/* append_filter() will free filter on error */
426 		str = strdup(*filter);
427 		if (!str)
428 			return -1;
429 	}
430 	open_parens = open;
431 
432 	ret = append_filter(&str, &state, &open_parens,
433 			    event, type, field, compare, val);
434 	if (!ret) {
435 		free(*filter);
436 		*filter = str;
437 	}
438 
439 	return ret;
440 }
441 
error_msg(char ** err,char * str,const char * filter,int i,const char * msg)442 static int error_msg(char **err, char *str,
443 		     const char *filter, int i, const char *msg)
444 {
445 	char ws[i+2];
446 	char *errmsg;
447 
448 	free(str);
449 
450 	/* msg is NULL for parsing append_filter failing */
451 	if (!msg) {
452 		switch(errno) {
453 		case ENODEV:
454 			msg = "field not valid";
455 			break;
456 		default:
457 			msg = "Invalid filter";
458 
459 		}
460 	} else
461 		errno = EINVAL;
462 
463 	if (!err)
464 		return -1;
465 
466 	if (!filter) {
467 		*err = strdup(msg);
468 		return -1;
469 	}
470 
471 	memset(ws, ' ', i);
472 	ws[i] = '^';
473 	ws[i+1] = '\0';
474 
475 	errmsg = strdup(filter);
476 	errmsg = append_string(errmsg, "\n", ws);
477 	errmsg = append_string(errmsg, "\n", msg);
478 	errmsg = append_string(errmsg, NULL, "\n");
479 
480 	*err = errmsg;
481 	return -1;
482 }
483 
get_field_end(const char * filter,int i,int * end)484 static int get_field_end(const char *filter, int i, int *end)
485 {
486 	int start_i = i;
487 
488 	for (; filter[i]; i++) {
489 		switch(filter[i]) {
490 		case '0' ... '9':
491 			if (i == start_i)
492 				return 0;
493 			/* Fall through */
494 		case 'a' ... 'z':
495 		case 'A' ... 'Z':
496 		case '_':
497 			continue;
498 		default:
499 			*end = i;
500 			return i - start_i;
501 		}
502 	}
503 	*end = i;
504 	return i - start_i;
505 }
506 
get_compare(const char * filter,int i,enum tracefs_compare * cmp)507 static int get_compare(const char *filter, int i, enum tracefs_compare *cmp)
508 {
509 	int start_i = i;
510 
511 	for (; filter[i]; i++) {
512 		if (!isspace(filter[i]))
513 			break;
514 	}
515 
516 	switch(filter[i]) {
517 	case '=':
518 		if (filter[i+1] != '=')
519 			goto err;
520 		*cmp = TRACEFS_COMPARE_EQ;
521 		i++;
522 		break;
523 	case '!':
524 		if (filter[i+1] == '=') {
525 			*cmp = TRACEFS_COMPARE_NE;
526 			i++;
527 			break;
528 		}
529 		if (filter[i+1] == '~') {
530 			/* todo! */
531 		}
532 		goto err;
533 	case '>':
534 		if (filter[i+1] == '=') {
535 			*cmp = TRACEFS_COMPARE_GE;
536 			i++;
537 			break;
538 		}
539 		*cmp = TRACEFS_COMPARE_GT;
540 		break;
541 	case '<':
542 		if (filter[i+1] == '=') {
543 			*cmp = TRACEFS_COMPARE_LE;
544 			i++;
545 			break;
546 		}
547 		*cmp = TRACEFS_COMPARE_LT;
548 		break;
549 	case '~':
550 		*cmp = TRACEFS_COMPARE_RE;
551 		break;
552 	case '&':
553 		*cmp = TRACEFS_COMPARE_AND;
554 		break;
555 	default:
556 		goto err;
557 	}
558 	i++;
559 
560 	for (; filter[i]; i++) {
561 		if (!isspace(filter[i]))
562 			break;
563 	}
564 	return i - start_i;
565  err:
566 	return start_i - i; /* negative or zero */
567 }
568 
get_val_end(const char * filter,int i,int * end)569 static int get_val_end(const char *filter, int i, int *end)
570 {
571 	bool backslash = false;
572 	int start_i = i;
573 	int quote;
574 
575 	switch (filter[i]) {
576 	case '0':
577 		i++;
578 		if (tolower(filter[i+1]) != 'x' &&
579 		    !isdigit(filter[i+1]))
580 			break;
581 		/* fall through */
582 	case '1' ... '9':
583 		switch (tolower(filter[i])) {
584 		case 'x':
585 			for (i++; filter[i]; i++) {
586 				if (!isxdigit(filter[i]))
587 					break;
588 			}
589 			break;
590 		case '0':
591 			for (i++; filter[i]; i++) {
592 				if (filter[i] < '0' ||
593 				    filter[i] > '7')
594 					break;
595 			}
596 			break;
597 		default:
598 			for (i++; filter[i]; i++) {
599 				if (!isdigit(filter[i]))
600 					break;
601 			}
602 			break;
603 		}
604 		break;
605 	case '"':
606 	case '\'':
607 		quote = filter[i];
608 		for (i++; filter[i]; i++) {
609 			if (backslash) {
610 				backslash = false;
611 				continue;
612 			}
613 			switch (filter[i]) {
614 			case '\\':
615 				backslash = true;
616 				continue;
617 			case '"':
618 			case '\'':
619 				if (filter[i] == quote)
620 					break;
621 				continue;
622 			default:
623 				continue;
624 			}
625 			break;
626 		}
627 		if (filter[i])
628 			i++;
629 		break;
630 	default:
631 		break;
632 	}
633 
634 	*end = i;
635 	return i - start_i;
636 }
637 
638 /**
639  * tracefs_filter_string_verify - verify a given filter works for an event
640  * @event: The event to test the given filter for
641  * @filter: The filter to test
642  * @err: Error message for syntax errors (NULL to ignore)
643  *
644  * Parse the @filter to verify that it is valid for the given @event.
645  *
646  * Returns 0 on succes and -1 on error, and except for memory allocation
647  * errors, @err will be allocated with an error message. It must
648  * be freed with free().
649  */
tracefs_filter_string_verify(struct tep_event * event,const char * filter,char ** err)650 int tracefs_filter_string_verify(struct tep_event *event, const char *filter,
651 				 char **err)
652 {
653 	enum tracefs_filter filter_type;
654 	enum tracefs_compare compare;
655 	char *str = NULL;
656 	char buf[(filter ? strlen(filter) : 0) + 1];
657 	char *field;
658 	char *val;
659 	unsigned int state = 0;
660 	unsigned int open = 0;
661 	int len;
662 	int end;
663 	int n;
664 	int i;
665 
666 	if (!filter)
667 		return error_msg(err, str, NULL, 0, "No filter given");
668 
669 	len = strlen(filter);
670 
671 	for (i = 0; i < len; i++) {
672 		field = NULL;
673 		val = NULL;
674 		compare = 0;
675 
676 		switch (filter[i]) {
677 		case '(':
678 			filter_type = TRACEFS_FILTER_OPEN_PAREN;
679 			break;
680 		case ')':
681 			filter_type = TRACEFS_FILTER_CLOSE_PAREN;
682 			break;
683 		case '!':
684 			filter_type = TRACEFS_FILTER_NOT;
685 			break;
686 		case '&':
687 		case '|':
688 
689 			if (filter[i] == filter[i+1]) {
690 				i++;
691 				if (filter[i] == '&')
692 					filter_type = TRACEFS_FILTER_AND;
693 				else
694 					filter_type = TRACEFS_FILTER_OR;
695 				break;
696 			}
697 			if (filter[i] == '|')
698 				return error_msg(err, str, filter, i,
699 						 "Invalid op");
700 
701 			return error_msg(err, str, filter, i,
702 					 "Invalid location for '&'");
703 		default:
704 			if (isspace(filter[i]))
705 				continue;
706 
707 			field = buf;
708 
709 			n = get_field_end(filter, i, &end);
710 			if (!n)
711 				return error_msg(err, str, filter, i,
712 						 "Invalid field name");
713 
714 			strncpy(field, filter+i, n);
715 
716 			i += n;
717 			field[n++] = '\0';
718 
719 			val = field + n;
720 
721 			n = get_compare(filter, i, &compare);
722 			if (n <= 0)
723 				return error_msg(err, str, filter, i - n,
724 						 "Invalid compare");
725 
726 			i += n;
727 			get_val_end(filter, i, &end);
728 			n = end - i;
729 			if (!n)
730 				return error_msg(err, str, filter, i,
731 						 "Invalid value");
732 			strncpy(val, filter + i, n);
733 			val[n] = '\0';
734 			i += n - 1;
735 
736 			filter_type = TRACEFS_FILTER_COMPARE;
737 			break;
738 		}
739 		n = append_filter(&str, &state, &open,
740 				    event, filter_type, field, compare, val);
741 
742 		if (n < 0)
743 			return error_msg(err, str, filter, i, NULL);
744 	}
745 
746 	if (open)
747 		return error_msg(err, str, filter, i,
748 				 "Not enough closed parenthesis");
749 	switch (state) {
750 	case S_COMPARE:
751 	case S_CLOSE_PAREN:
752 		break;
753 	default:
754 		return error_msg(err, str, filter, i,
755 				 "Unfinished filter");
756 	}
757 
758 	free(str);
759 	return 0;
760 }
761 
762 /**
763  * tracefs_event_filter_apply - apply given filter on event in given instance
764  * @instance: The instance in which the filter will be applied (NULL for toplevel).
765  * @event: The event to apply the filter on.
766  * @filter: The filter to apply.
767  *
768  * Apply the @filter to given @event in givem @instance. The @filter string
769  * should be created with tracefs_filter_string_append().
770  *
771  * Returns 0 on succes and -1 on error.
772  */
tracefs_event_filter_apply(struct tracefs_instance * instance,struct tep_event * event,const char * filter)773 int tracefs_event_filter_apply(struct tracefs_instance *instance,
774 			       struct tep_event *event, const char *filter)
775 {
776 	return tracefs_event_file_write(instance, event->system, event->name,
777 					"filter", filter);
778 }
779 
780 /**
781  * tracefs_event_filter_clear - clear the filter on event in given instance
782  * @instance: The instance in which the filter will be applied (NULL for toplevel).
783  * @event: The event to apply the filter on.
784  *
785  * Returns 0 on succes and -1 on error.
786  */
tracefs_event_filter_clear(struct tracefs_instance * instance,struct tep_event * event)787 int tracefs_event_filter_clear(struct tracefs_instance *instance,
788 			       struct tep_event *event)
789 {
790 	return tracefs_event_file_write(instance, event->system, event->name,
791 					"filter", "0");
792 }
793 
794 /** Deprecated **/
tracefs_event_append_filter(struct tep_event * event,char ** filter,enum tracefs_filter type,const char * field,enum tracefs_compare compare,const char * val)795 int tracefs_event_append_filter(struct tep_event *event, char **filter,
796 				enum tracefs_filter type,
797 				const char *field, enum tracefs_compare compare,
798 				const char *val)
799 {
800 	return tracefs_filter_string_append(event, filter, type, field,
801 					    compare, val);
802 }
tracefs_event_verify_filter(struct tep_event * event,const char * filter,char ** err)803 int tracefs_event_verify_filter(struct tep_event *event, const char *filter,
804 				char **err)
805 {
806 	return tracefs_filter_string_verify(event, filter, err);
807 }
808