@@ -71,4 +71,18 @@ struct tracefs_options_mask *
enabled_opts_mask(struct tracefs_instance *instance);
char **trace_list_create_empty(void);
+
+char *append_string(char *str, const char *delim, const char *add);
+int trace_test_state(int state);
+bool trace_verify_event_field(struct tep_event *event,
+ const char *field_name,
+ const struct tep_format_field **ptr_field);
+int 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);
+
#endif /* _TRACE_FS_LOCAL_H */
@@ -313,7 +313,7 @@ enum tracefs_synth_calc {
TRACEFS_SYNTH_ADD,
};
-enum tracefs_synth_compare {
+enum tracefs_compare {
TRACEFS_COMPARE_EQ,
TRACEFS_COMPARE_NE,
TRACEFS_COMPARE_GT,
@@ -333,6 +333,13 @@ enum tracefs_filter {
TRACEFS_FILTER_CLOSE_PAREN,
};
+int tracefs_event_append_filter(struct tep_event *event, char **filter,
+ enum tracefs_filter type,
+ const char *field, enum tracefs_compare compare,
+ const char *val);
+int tracefs_event_verify_filter(struct tep_event *event, const char *filter,
+ char **err);
+
#define TRACEFS_TIMESTAMP "common_timestamp"
#define TRACEFS_TIMESTAMP_USECS "common_timestamp.usecs"
@@ -363,12 +370,12 @@ int tracefs_synth_add_end_field(struct tracefs_synth *synth,
int tracefs_synth_append_start_filter(struct tracefs_synth *synth,
enum tracefs_filter type,
const char *field,
- enum tracefs_synth_compare compare,
+ enum tracefs_compare compare,
const char *val);
int tracefs_synth_append_end_filter(struct tracefs_synth *synth,
enum tracefs_filter type,
const char *field,
- enum tracefs_synth_compare compare,
+ enum tracefs_compare compare,
const char *val);
int tracefs_synth_create(struct tracefs_instance *instance,
struct tracefs_synth *synth);
@@ -10,6 +10,7 @@ OBJS += tracefs-tools.o
OBJS += tracefs-marker.o
OBJS += tracefs-kprobes.o
OBJS += tracefs-hist.o
+OBJS += tracefs-filter.o
OBJS := $(OBJS:%.o=$(bdir)/%.o)
DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
new file mode 100644
@@ -0,0 +1,747 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <trace-seq.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+enum {
+ S_START,
+ S_COMPARE,
+ S_NOT,
+ S_CONJUNCTION,
+ S_OPEN_PAREN,
+ S_CLOSE_PAREN,
+};
+
+static const struct tep_format_field common_timestamp = {
+ .type = "u64",
+ .name = "common_timestamp",
+ .size = 8,
+};
+
+static const struct tep_format_field common_timestamp_usecs = {
+ .type = "u64",
+ .name = "common_timestamp.usecs",
+ .size = 8,
+};
+
+/*
+ * This also must be able to accept fields that are OK via the histograms,
+ * such as common_timestamp.
+ */
+static const struct tep_format_field *get_event_field(struct tep_event *event,
+ const char *field_name)
+{
+ if (!strcmp(field_name, TRACEFS_TIMESTAMP))
+ return &common_timestamp;
+
+ if (!strcmp(field_name, TRACEFS_TIMESTAMP_USECS))
+ return &common_timestamp_usecs;
+
+ return tep_find_any_field(event, field_name);
+}
+
+__hidden bool
+trace_verify_event_field(struct tep_event *event,
+ const char *field_name,
+ const struct tep_format_field **ptr_field)
+{
+ const struct tep_format_field *field;
+
+ field = get_event_field(event, field_name);
+ if (!field) {
+ errno = ENODEV;
+ return false;
+ }
+
+ if (ptr_field)
+ *ptr_field = field;
+
+ return true;
+}
+
+__hidden int trace_test_state(int state)
+{
+ switch (state) {
+ case S_START:
+ case S_CLOSE_PAREN:
+ case S_COMPARE:
+ return 0;
+ }
+
+ errno = EBADE;
+ return -1;
+}
+
+static int 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)
+{
+ const struct tep_format_field *field;
+ bool is_string;
+ char *conj = "||";
+ char *tmp;
+
+ switch (type) {
+ case TRACEFS_FILTER_COMPARE:
+ switch (*state) {
+ case S_START:
+ case S_OPEN_PAREN:
+ case S_CONJUNCTION:
+ case S_NOT:
+ break;
+ default:
+ goto inval;
+ }
+ break;
+
+ case TRACEFS_FILTER_AND:
+ conj = "&&";
+ /* Fall through */
+ case TRACEFS_FILTER_OR:
+ switch (*state) {
+ case S_COMPARE:
+ case S_CLOSE_PAREN:
+ break;
+ default:
+ goto inval;
+ }
+ /* Don't lose old filter on failure */
+ tmp = strdup(*filter);
+ if (!tmp)
+ return -1;
+ tmp = append_string(tmp, NULL, conj);
+ if (!tmp)
+ return -1;
+ free(*filter);
+ *filter = tmp;
+ *state = S_CONJUNCTION;
+ return 0;
+
+ case TRACEFS_FILTER_NOT:
+ switch (*state) {
+ case S_START:
+ case S_OPEN_PAREN:
+ case S_CONJUNCTION:
+ case S_NOT:
+ break;
+ default:
+ goto inval;
+ }
+ if (*filter) {
+ tmp = strdup(*filter);
+ tmp = append_string(tmp, NULL, "!");
+ } else {
+ tmp = strdup("!");
+ }
+ if (!tmp)
+ return -1;
+ free(*filter);
+ *filter = tmp;
+ *state = S_NOT;
+ return 0;
+
+ case TRACEFS_FILTER_OPEN_PAREN:
+ switch (*state) {
+ case S_START:
+ case S_OPEN_PAREN:
+ case S_NOT:
+ case S_CONJUNCTION:
+ break;
+ default:
+ goto inval;
+ }
+ if (*filter) {
+ tmp = strdup(*filter);
+ tmp = append_string(tmp, NULL, "(");
+ } else {
+ tmp = strdup("(");
+ }
+ if (!tmp)
+ return -1;
+ free(*filter);
+ *filter = tmp;
+ *state = S_OPEN_PAREN;
+ (*open_parens)++;
+ return 0;
+
+ case TRACEFS_FILTER_CLOSE_PAREN:
+ switch (*state) {
+ case S_CLOSE_PAREN:
+ case S_COMPARE:
+ break;
+ default:
+ goto inval;
+ }
+ if (!*open_parens)
+ goto inval;
+
+ tmp = strdup(*filter);
+ if (!tmp)
+ return -1;
+ tmp = append_string(tmp, NULL, ")");
+ if (!tmp)
+ return -1;
+ free(*filter);
+ *filter = tmp;
+ *state = S_CLOSE_PAREN;
+ (*open_parens)--;
+ return 0;
+ }
+
+ if (!field_name || !val)
+ goto inval;
+
+ if (!trace_verify_event_field(event, field_name, &field))
+ return -1;
+
+ is_string = field->flags & TEP_FIELD_IS_STRING;
+
+ if (!is_string && (field->flags & TEP_FIELD_IS_ARRAY))
+ goto inval;
+
+ if (*filter) {
+ tmp = strdup(*filter);
+ if (!tmp)
+ return -1;
+ tmp = append_string(tmp, NULL, field_name);
+ } else {
+ tmp = strdup(field_name);
+ }
+
+ switch (compare) {
+ case TRACEFS_COMPARE_EQ: tmp = append_string(tmp, NULL, " == "); break;
+ case TRACEFS_COMPARE_NE: tmp = append_string(tmp, NULL, " != "); break;
+ case TRACEFS_COMPARE_RE:
+ if (!is_string)
+ goto inval;
+ tmp = append_string(tmp, NULL, "~");
+ break;
+ default:
+ if (is_string)
+ goto inval;
+ }
+
+ switch (compare) {
+ case TRACEFS_COMPARE_GT: tmp = append_string(tmp, NULL, " > "); break;
+ case TRACEFS_COMPARE_GE: tmp = append_string(tmp, NULL, " >= "); break;
+ case TRACEFS_COMPARE_LT: tmp = append_string(tmp, NULL, " < "); break;
+ case TRACEFS_COMPARE_LE: tmp = append_string(tmp, NULL, " <= "); break;
+ case TRACEFS_COMPARE_AND: tmp = append_string(tmp, NULL, " & "); break;
+ default: break;
+ }
+
+ tmp = append_string(tmp, NULL, val);
+
+ if (!tmp)
+ return -1;
+
+ free(*filter);
+ *filter = tmp;
+ *state = S_COMPARE;
+
+ return 0;
+inval:
+ errno = EINVAL;
+ return -1;
+}
+
+static int count_parens(char *filter, unsigned int *state)
+{
+ bool backslash = false;
+ int quote = 0;
+ int open = 0;
+ int i;
+
+ if (!filter)
+ return 0;
+
+ for (i = 0; filter[i]; i++) {
+ if (quote) {
+ if (backslash)
+ backslash = false;
+ else if (filter[i] == '\\')
+ backslash = true;
+ else if (quote == filter[i])
+ quote = 0;
+ continue;
+ }
+
+ switch (filter[i]) {
+ case '(':
+ *state = S_OPEN_PAREN;
+ open++;
+ break;
+ case ')':
+ *state = S_CLOSE_PAREN;
+ open--;
+ break;
+ case '\'':
+ case '"':
+ *state = S_COMPARE;
+ quote = filter[i];
+ break;
+ case '!':
+ switch (filter[i+1]) {
+ case '=':
+ case '~':
+ *state = S_COMPARE;
+ i++;
+ break;
+ default:
+ *state = S_NOT;
+ }
+ break;
+ case '&':
+ case '|':
+ if (filter[i] == filter[i+1]) {
+ *state = S_CONJUNCTION;
+ i++;
+ break;
+ }
+ /* Fall through */
+ case '0' ... '9':
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ case '_': case '+': case '-': case '*': case '/':
+ *state = S_COMPARE;
+ break;
+ }
+ }
+ return open;
+}
+
+__hidden int 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)
+{
+ return append_filter(filter, state, open_parens, event, type,
+ field_name, compare, val);
+}
+
+/**
+ * tracefs_event_append_filter - create or append a filter for an event
+ * @event: tep_event to create / append a filter for
+ * @filter: Pointer to string to append to (pointer to NULL to create)
+ * @type: The type of element to add to the filter
+ * @field: For @type == TRACEFS_FILTER_COMPARE, the field to compare
+ * @compare: For @type == TRACEFS_FILTER_COMPARE, how to compare @field to @val
+ * @val: For @type == TRACEFS_FILTER_COMPARE, what value @field is to be
+ *
+ * This will put together a filter string for the starting event
+ * of @synth. It check to make sure that what is added is correct compared
+ * to the filter that is already built.
+ *
+ * @type can be:
+ * TRACEFS_FILTER_COMPARE: See below
+ * TRACEFS_FILTER_AND: Append "&&" to the filter
+ * TRACEFS_FILTER_OR: Append "||" to the filter
+ * TRACEFS_FILTER_NOT: Append "!" to the filter
+ * TRACEFS_FILTER_OPEN_PAREN: Append "(" to the filter
+ * TRACEFS_FILTER_CLOSE_PAREN: Append ")" to the filter
+ *
+ * For all types except TRACEFS_FILTER_COMPARE, the @field, @compare,
+ * and @val are ignored.
+ *
+ * For @type == TRACEFS_FILTER_COMPARE.
+ *
+ * @field is the name of the field for the start event to compare.
+ * If it is not a field for the start event, this return an
+ * error.
+ *
+ * @compare can be one of:
+ * TRACEFS_COMPARE_EQ: Test @field == @val
+ * TRACEFS_COMPARE_NE: Test @field != @val
+ * TRACEFS_COMPARE_GT: Test @field > @val
+ * TRACEFS_COMPARE_GE: Test @field >= @val
+ * TRACEFS_COMPARE_LT: Test @field < @val
+ * TRACEFS_COMPARE_LE: Test @field <= @val
+ * TRACEFS_COMPARE_RE: Test @field ~ @val
+ * TRACEFS_COMPARE_AND: Test @field & @val
+ *
+ * If the @field is of type string, and @compare is not
+ * TRACEFS_COMPARE_EQ, TRACEFS_COMPARE_NE or TRACEFS_COMPARE_RE,
+ * then this will return an error.
+ *
+ * Various other checks are made, for instance, if more CLOSE_PARENs
+ * are added than existing OPEN_PARENs. Or if AND is added after an
+ * OPEN_PAREN or another AND or an OR or a NOT.
+ *
+ * Returns 0 on success and -1 on failure.
+ */
+int tracefs_event_append_filter(struct tep_event *event, char **filter,
+ enum tracefs_filter type,
+ const char *field, enum tracefs_compare compare,
+ const char *val)
+{
+ unsigned int open_parens;
+ unsigned int state = 0;
+ char *str = NULL;
+ int open;
+ int ret;
+
+ if (!filter) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ open = count_parens(*filter, &state);
+ if (open < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*filter) {
+ /* append_filter() will free filter on error */
+ str = strdup(*filter);
+ if (!str)
+ return -1;
+ }
+ open_parens = open;
+
+ ret = append_filter(&str, &state, &open_parens,
+ event, type, field, compare, val);
+ if (!ret) {
+ free(*filter);
+ *filter = str;
+ }
+ return 0;
+}
+
+static int error_msg(char **err, char *str,
+ const char *filter, int i, const char *msg)
+{
+ char ws[i+2];
+ char *errmsg;
+
+ free(str);
+
+ /* msg is NULL for parsing append_filter failing */
+ if (!msg) {
+ switch(errno) {
+ case ENODEV:
+ msg = "field not valid";
+ break;
+ default:
+ msg = "Invalid filter";
+
+ }
+ } else
+ errno = EINVAL;
+
+ if (!err)
+ return -1;
+
+ if (!filter) {
+ *err = strdup(msg);
+ return -1;
+ }
+
+ memset(ws, ' ', i);
+ ws[i] = '^';
+ ws[i+1] = '\0';
+
+ errmsg = strdup(filter);
+ errmsg = append_string(errmsg, "\n", ws);
+ errmsg = append_string(errmsg, "\n", msg);
+ errmsg = append_string(errmsg, NULL, "\n");
+
+ *err = errmsg;
+ return -1;
+}
+
+static int get_field_end(const char *filter, int i, int *end)
+{
+ int start_i = i;
+
+ for (; filter[i]; i++) {
+ switch(filter[i]) {
+ case '0' ... '9':
+ if (i == start_i)
+ return 0;
+ /* Fall through */
+ case 'a' ... 'z':
+ case 'A' ... 'Z':
+ case '_':
+ continue;
+ default:
+ *end = i;
+ return i - start_i;
+ }
+ }
+ *end = i;
+ return i - start_i;
+}
+
+static int get_compare(const char *filter, int i, enum tracefs_compare *cmp)
+{
+ int start_i = i;
+
+ for (; filter[i]; i++) {
+ if (!isspace(filter[i]))
+ break;
+ }
+
+ switch(filter[i]) {
+ case '=':
+ if (filter[i+1] != '=')
+ goto err;
+ *cmp = TRACEFS_COMPARE_EQ;
+ i++;
+ break;
+ case '!':
+ if (filter[i+1] == '=') {
+ *cmp = TRACEFS_COMPARE_NE;
+ i++;
+ break;
+ }
+ if (filter[i+1] == '~') {
+ /* todo! */
+ }
+ goto err;
+ case '>':
+ if (filter[i+1] == '=') {
+ *cmp = TRACEFS_COMPARE_GE;
+ i++;
+ break;
+ }
+ *cmp = TRACEFS_COMPARE_GT;
+ break;
+ case '<':
+ if (filter[i+1] == '=') {
+ *cmp = TRACEFS_COMPARE_LE;
+ i++;
+ break;
+ }
+ *cmp = TRACEFS_COMPARE_LT;
+ break;
+ case '~':
+ *cmp = TRACEFS_COMPARE_RE;
+ break;
+ case '&':
+ *cmp = TRACEFS_COMPARE_AND;
+ break;
+ default:
+ goto err;
+ }
+ i++;
+
+ for (; filter[i]; i++) {
+ if (!isspace(filter[i]))
+ break;
+ }
+ return i - start_i;
+ err:
+ return start_i - i; /* negative or zero */
+}
+
+static int get_val_end(const char *filter, int i, int *end)
+{
+ bool backslash = false;
+ int start_i = i;
+ int quote;
+
+ switch (filter[i]) {
+ case '0':
+ i++;
+ if (tolower(filter[i+1]) != 'x' &&
+ !isdigit(filter[i+1]))
+ break;
+ /* fall through */
+ case '1' ... '9':
+ switch (tolower(filter[i])) {
+ case 'x':
+ for (i++; filter[i]; i++) {
+ if (!isxdigit(filter[i]))
+ break;
+ }
+ break;
+ case '0':
+ for (i++; filter[i]; i++) {
+ if (filter[i] < '0' ||
+ filter[i] > '7')
+ break;
+ }
+ break;
+ default:
+ for (i++; filter[i]; i++) {
+ if (!isdigit(filter[i]))
+ break;
+ }
+ break;
+ }
+ break;
+ case '"':
+ case '\'':
+ quote = filter[i];
+ for (i++; filter[i]; i++) {
+ if (backslash) {
+ backslash = false;
+ continue;
+ }
+ switch (filter[i]) {
+ case '\\':
+ backslash = true;
+ continue;
+ case '"':
+ case '\'':
+ if (filter[i] == quote)
+ break;
+ continue;
+ default:
+ continue;
+ }
+ break;
+ }
+ if (filter[i])
+ i++;
+ break;
+ default:
+ break;
+ }
+
+ *end = i;
+ return i - start_i;
+}
+
+/**
+ * tracefs_event_verify_filter - verify a given filter works for an event
+ * @event: The event to test the given filter for
+ * @filter: The filter to test
+ * @err: Error message for syntax errors (NULL to ignore)
+ *
+ * Parse the @filter to verify that it is valid for the given @event.
+ *
+ * Returns 0 on succes and -1 on error, and except for memory allocation
+ * errors, @err will be allocated with an error message. It must
+ * be freed with free().
+ */
+int tracefs_event_verify_filter(struct tep_event *event, const char *filter,
+ char **err)
+{
+ enum tracefs_filter filter_type;
+ enum tracefs_compare compare;
+ char *str = NULL;
+ char buf[(filter ? strlen(filter) : 0) + 1];
+ char *field;
+ char *val;
+ unsigned int state = 0;
+ unsigned int open = 0;
+ int len;
+ int end;
+ int n;
+ int i;
+
+ if (!filter)
+ return error_msg(err, str, NULL, 0, "No filter given");
+
+ len = strlen(filter);
+
+ for (i = 0; i < len; i++) {
+ field = NULL;
+ val = NULL;
+ compare = 0;
+
+ switch (filter[i]) {
+ case '(':
+ filter_type = TRACEFS_FILTER_OPEN_PAREN;
+ break;
+ case ')':
+ filter_type = TRACEFS_FILTER_CLOSE_PAREN;
+ break;
+ case '!':
+ filter_type = TRACEFS_FILTER_NOT;
+ break;
+ case '&':
+ case '|':
+
+ if (filter[i] == filter[i+1]) {
+ i++;
+ if (filter[i] == '&')
+ filter_type = TRACEFS_FILTER_AND;
+ else
+ filter_type = TRACEFS_FILTER_OR;
+ break;
+ }
+ if (filter[i] == '|')
+ return error_msg(err, str, filter, i,
+ "Invalid op");
+
+ return error_msg(err, str, filter, i,
+ "Invalid location for '&'");
+ default:
+ if (isspace(filter[i]))
+ continue;
+
+ field = buf;
+
+ n = get_field_end(filter, i, &end);
+ if (!n)
+ return error_msg(err, str, filter, i,
+ "Invalid field name");
+
+ strncpy(field, filter+i, n);
+
+ i += n;
+ field[n++] = '\0';
+
+ val = field + n;
+
+ n = get_compare(filter, i, &compare);
+ if (n <= 0)
+ return error_msg(err, str, filter, i - n,
+ "Invalid compare");
+
+ i += n;
+ get_val_end(filter, i, &end);
+ n = end - i;
+ if (!n)
+ return error_msg(err, str, filter, i,
+ "Invalid value");
+ strncpy(val, filter + i, n);
+ val[n] = '\0';
+ i += n - 1;
+
+ filter_type = TRACEFS_FILTER_COMPARE;
+ break;
+ }
+ n = append_filter(&str, &state, &open,
+ event, filter_type, field, compare, val);
+
+ if (n < 0)
+ return error_msg(err, str, filter, i, NULL);
+ }
+
+ if (open)
+ return error_msg(err, str, filter, i,
+ "Not enough closed parenthesis");
+ switch (state) {
+ case S_COMPARE:
+ case S_CLOSE_PAREN:
+ break;
+ default:
+ return error_msg(err, str, filter, i,
+ "Unfinished filter");
+ }
+
+ free(str);
+ return 0;
+}
@@ -568,18 +568,6 @@ struct tracefs_synth {
int arg_cnt;
};
-static const struct tep_format_field common_timestamp = {
- .type = "u64",
- .name = "common_timestamp",
- .size = 8,
-};
-
-static const struct tep_format_field common_timestamp_usecs = {
- .type = "u64",
- .name = "common_timestamp.usecs",
- .size = 8,
-};
-
/**
* tracefs_synth_free - free the resources alloced to a synth
* @synth: The tracefs_synth descriptor
@@ -609,18 +597,6 @@ void tracefs_synth_free(struct tracefs_synth *synth)
free(synth);
}
-static const struct tep_format_field *get_event_field(struct tep_event *event,
- const char *field_name)
-{
- if (!strcmp(field_name, TRACEFS_TIMESTAMP))
- return &common_timestamp;
-
- if (!strcmp(field_name, TRACEFS_TIMESTAMP_USECS))
- return &common_timestamp_usecs;
-
- return tep_find_any_field(event, field_name);
-}
-
static bool verify_event_fields(struct tep_event *start_event,
struct tep_event *end_event,
const char *start_field_name,
@@ -630,14 +606,14 @@ static bool verify_event_fields(struct tep_event *start_event,
const struct tep_format_field *start_field;
const struct tep_format_field *end_field;
- start_field = get_event_field(start_event, start_field_name);
- if (!start_field)
- goto nodev;
+ if (!trace_verify_event_field(start_event, start_field_name,
+ &start_field))
+ return false;
if (end_event) {
- end_field = get_event_field(end_event, end_field_name);
- if (!start_field)
- goto nodev;
+ if (!trace_verify_event_field(end_event, end_field_name,
+ &end_field))
+ return false;
if (start_field->flags != end_field->flags ||
start_field->size != end_field->size) {
@@ -650,12 +626,9 @@ static bool verify_event_fields(struct tep_event *start_event,
*ptr_start_field = start_field;
return true;
- nodev:
- errno = ENODEV;
- return false;
}
-static char *append_string(char *str, const char *space, const char *add)
+__hidden char *append_string(char *str, const char *space, const char *add)
{
char *new;
int len;
@@ -1110,8 +1083,7 @@ int tracefs_synth_add_start_field(struct tracefs_synth *synth,
if (!name)
name = start_field;
- if (!verify_event_fields(synth->start_event, NULL,
- start_field, NULL, &field))
+ if (!trace_verify_event_field(synth->start_event, start_field, &field))
return -1;
start_arg = new_arg(synth);
@@ -1163,8 +1135,7 @@ int tracefs_synth_add_end_field(struct tracefs_synth *synth,
if (!name)
name = end_field;
- if (!verify_event_fields(synth->end_event, NULL,
- end_field, NULL, &field))
+ if (!trace_verify_event_field(synth->end_event, end_field, &field))
return -1;
ret = add_var(&synth->end_vars, name, end_field, false);
@@ -1177,192 +1148,6 @@ int tracefs_synth_add_end_field(struct tracefs_synth *synth,
return ret;
}
-enum {
- S_START,
- S_COMPARE,
- S_NOT,
- S_CONJUNCTION,
- S_OPEN_PAREN,
- S_CLOSE_PAREN,
-};
-
-static int append_synth_filter(char **filter, unsigned int *state,
- unsigned int *open_parens,
- struct tep_event *event,
- enum tracefs_filter type,
- const char *field_name,
- enum tracefs_synth_compare compare,
- const char *val)
-{
- const struct tep_format_field *field;
- bool is_string;
- char *conj = "||";
- char *tmp;
-
- switch (type) {
- case TRACEFS_FILTER_COMPARE:
- switch (*state) {
- case S_START:
- case S_OPEN_PAREN:
- case S_CONJUNCTION:
- case S_NOT:
- break;
- default:
- goto inval;
- }
- break;
-
- case TRACEFS_FILTER_AND:
- conj = "&&";
- /* Fall through */
- case TRACEFS_FILTER_OR:
- switch (*state) {
- case S_COMPARE:
- case S_CLOSE_PAREN:
- break;
- default:
- goto inval;
- }
- /* Don't lose old filter on failure */
- tmp = strdup(*filter);
- if (!tmp)
- return -1;
- tmp = append_string(tmp, NULL, conj);
- if (!tmp)
- return -1;
- free(*filter);
- *filter = tmp;
- *state = S_CONJUNCTION;
- return 0;
-
- case TRACEFS_FILTER_NOT:
- switch (*state) {
- case S_START:
- case S_OPEN_PAREN:
- case S_CONJUNCTION:
- case S_NOT:
- break;
- default:
- goto inval;
- }
- if (*filter) {
- tmp = strdup(*filter);
- tmp = append_string(tmp, NULL, "!");
- } else {
- tmp = strdup("!");
- }
- if (!tmp)
- return -1;
- free(*filter);
- *filter = tmp;
- *state = S_NOT;
- return 0;
-
- case TRACEFS_FILTER_OPEN_PAREN:
- switch (*state) {
- case S_START:
- case S_OPEN_PAREN:
- case S_NOT:
- case S_CONJUNCTION:
- break;
- default:
- goto inval;
- }
- if (*filter) {
- tmp = strdup(*filter);
- tmp = append_string(tmp, NULL, "(");
- } else {
- tmp = strdup("(");
- }
- if (!tmp)
- return -1;
- free(*filter);
- *filter = tmp;
- *state = S_OPEN_PAREN;
- (*open_parens)++;
- return 0;
-
- case TRACEFS_FILTER_CLOSE_PAREN:
- switch (*state) {
- case S_CLOSE_PAREN:
- case S_COMPARE:
- break;
- default:
- goto inval;
- }
- if (!*open_parens)
- goto inval;
-
- tmp = strdup(*filter);
- if (!tmp)
- return -1;
- tmp = append_string(tmp, NULL, ")");
- if (!tmp)
- return -1;
- free(*filter);
- *filter = tmp;
- *state = S_CLOSE_PAREN;
- (*open_parens)--;
- return 0;
- }
-
- if (!field_name || !val)
- goto inval;
-
- if (!verify_event_fields(event, NULL, field_name, NULL, &field))
- return -1;
-
- is_string = field->flags & TEP_FIELD_IS_STRING;
-
- if (!is_string && (field->flags & TEP_FIELD_IS_ARRAY))
- goto inval;
-
- if (*filter) {
- tmp = strdup(*filter);
- if (!tmp)
- return -1;
- tmp = append_string(tmp, NULL, field_name);
- } else {
- tmp = strdup(field_name);
- }
-
- switch (compare) {
- case TRACEFS_COMPARE_EQ: tmp = append_string(tmp, NULL, " == "); break;
- case TRACEFS_COMPARE_NE: tmp = append_string(tmp, NULL, " != "); break;
- case TRACEFS_COMPARE_RE:
- if (!is_string)
- goto inval;
- tmp = append_string(tmp, NULL, "~");
- break;
- default:
- if (is_string)
- goto inval;
- }
-
- switch (compare) {
- case TRACEFS_COMPARE_GT: tmp = append_string(tmp, NULL, " > "); break;
- case TRACEFS_COMPARE_GE: tmp = append_string(tmp, NULL, " >= "); break;
- case TRACEFS_COMPARE_LT: tmp = append_string(tmp, NULL, " < "); break;
- case TRACEFS_COMPARE_LE: tmp = append_string(tmp, NULL, " <= "); break;
- case TRACEFS_COMPARE_AND: tmp = append_string(tmp, NULL, " & "); break;
- default: break;
- }
-
- tmp = append_string(tmp, NULL, val);
-
- if (!tmp)
- return -1;
-
- free(*filter);
- *filter = tmp;
- *state = S_COMPARE;
-
- return 0;
-inval:
- errno = EINVAL;
- return -1;
-}
-
/**
* tracefs_synth_append_start_filter - create or append a filter
* @synth: The tracefs_synth descriptor
@@ -1415,10 +1200,10 @@ inval:
int tracefs_synth_append_start_filter(struct tracefs_synth *synth,
enum tracefs_filter type,
const char *field,
- enum tracefs_synth_compare compare,
+ enum tracefs_compare compare,
const char *val)
{
- return append_synth_filter(&synth->start_filter, &synth->start_state,
+ return trace_append_filter(&synth->start_filter, &synth->start_state,
&synth->start_parens,
synth->start_event,
type, field, compare, val);
@@ -1438,10 +1223,10 @@ int tracefs_synth_append_start_filter(struct tracefs_synth *synth,
int tracefs_synth_append_end_filter(struct tracefs_synth *synth,
enum tracefs_filter type,
const char *field,
- enum tracefs_synth_compare compare,
+ enum tracefs_compare compare,
const char *val)
{
- return append_synth_filter(&synth->end_filter, &synth->end_state,
+ return trace_append_filter(&synth->end_filter, &synth->end_state,
&synth->end_parens,
synth->end_event,
type, field, compare, val);
@@ -1564,23 +1349,10 @@ static char *append_filter(char *hist, char *filter, unsigned int parens)
return hist;
}
-static int test_state(int state)
-{
- switch (state) {
- case S_START:
- case S_CLOSE_PAREN:
- case S_COMPARE:
- return 0;
- }
-
- errno = EBADE;
- return -1;
-}
-
static int verify_state(struct tracefs_synth *synth)
{
- if (test_state(synth->start_state) < 0 ||
- test_state(synth->end_state) < 0)
+ if (trace_test_state(synth->start_state) < 0 ||
+ trace_test_state(synth->end_state) < 0)
return -1;
return 0;
}