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