@@ -418,6 +418,12 @@ static void add_errors(const char ***errs, const char *filter, int ret)
*errs = e;
}
+struct func_list {
+ struct func_list *next;
+ unsigned int start;
+ unsigned int end;
+};
+
struct func_filter {
const char *filter;
regex_t re;
@@ -534,18 +540,54 @@ static int write_filter(int fd, const char *filter, const char *module)
return 0;
}
+static int add_func(struct func_list ***next_func_ptr, unsigned int index)
+{
+ struct func_list **next_func = *next_func_ptr;
+ struct func_list *func_list = *next_func;
+
+ if (!func_list) {
+ func_list = calloc(1, sizeof(*func_list));
+ if (!func_list)
+ return -1;
+ func_list->start = index;
+ func_list->end = index;
+ *next_func = func_list;
+ return 0;
+ }
+
+ if (index == func_list->end + 1) {
+ func_list->end = index;
+ return 0;
+ }
+ *next_func_ptr = &func_list->next;
+ return add_func(next_func_ptr, index);
+}
+
+static void free_func_list(struct func_list *func_list)
+{
+ struct func_list *f;
+
+ while (func_list) {
+ f = func_list;
+ func_list = f->next;
+ free(f);
+ }
+}
+
enum match_type {
FILTER_CHECK,
FILTER_WRITE,
};
static int match_filters(int fd, struct func_filter *func_filters,
- const char *module, enum match_type type)
+ const char *module, struct func_list **func_list,
+ enum match_type type)
{
char *line = NULL;
size_t size = 0;
char *path;
FILE *fp;
+ int index = 0;
int ret = 1;
int mlen;
int i;
@@ -567,12 +609,16 @@ static int match_filters(int fd, struct func_filter *func_filters,
char *saveptr = NULL;
char *tok, *mtok;
int len = strlen(line);
+ bool first = true;
if (line[len - 1] == '\n')
line[len - 1] = '\0';
tok = strtok_r(line, " ", &saveptr);
if (!tok)
goto next;
+
+ index++;
+
if (module) {
mtok = strtok_r(line, " ", &saveptr);
if (!mtok)
@@ -585,8 +631,23 @@ static int match_filters(int fd, struct func_filter *func_filters,
case FILTER_CHECK:
/* Check, checks a list of filters */
for (i = 0; func_filters[i].filter; i++) {
- if (match(tok, &func_filters[i]))
+ /*
+ * If a match was found, still need to
+ * check if other filters would match
+ * to make sure that all filters have a
+ * match, as some filters may overlap.
+ */
+ if (!first && func_filters[i].set)
+ continue;
+ if (match(tok, &func_filters[i])) {
func_filters[i].set = true;
+ if (first) {
+ first = false;
+ ret = add_func(&func_list, index);
+ if (ret)
+ goto out;
+ }
+ }
}
break;
case FILTER_WRITE:
@@ -611,12 +672,13 @@ static int match_filters(int fd, struct func_filter *func_filters,
}
static int check_available_filters(struct func_filter *func_filters,
- const char *module, const char ***errs)
+ const char *module, const char ***errs,
+ struct func_list **func_list)
{
int ret;
int i;
- ret = match_filters(-1, func_filters, module, FILTER_CHECK);
+ ret = match_filters(-1, func_filters, module, func_list, FILTER_CHECK);
/* Return here if success or non filter error */
if (ret >= 0)
return ret;
@@ -633,7 +695,7 @@ static int check_available_filters(struct func_filter *func_filters,
static int set_regex_filter(int fd, struct func_filter *func_filter,
const char *module)
{
- return match_filters(fd, func_filter, module, FILTER_WRITE);
+ return match_filters(fd, func_filter, module, NULL, FILTER_WRITE);
}
static int controlled_write(int fd, struct func_filter *func_filters,
@@ -724,6 +786,49 @@ static struct func_filter *make_func_filters(const char **filters)
return NULL;
}
+static int write_number(int fd, unsigned int start, unsigned int end)
+{
+ char buf[64];
+ unsigned int i;
+ int n, ret;
+
+ for (i = start; i <= end; i++) {
+ n = snprintf(buf, 64, "%d ", i);
+ ret = write(fd, buf, n);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * This will try to write the first number, if that fails, it
+ * will assume that it is not supported and return 1.
+ * If the first write succeeds, but a following write fails, then
+ * the kernel does support this, but something else went wrong,
+ * in this case, return -1.
+ */
+static int write_func_list(int fd, struct func_list *list)
+{
+ int ret;
+
+ if (!list)
+ return 0;
+
+ ret = write_number(fd, list->start, list->end);
+ if (ret)
+ return 1; // try a different way
+ list = list->next;
+ while (list) {
+ ret = write_number(fd, list->start, list->end);
+ if (ret)
+ return -1;
+ list = list->next;
+ }
+ return 0;
+}
+
/**
* tracefs_function_filter - write to set_ftrace_filter file to trace
* particular functions
@@ -756,6 +861,7 @@ int tracefs_function_filter(struct tracefs_instance *instance, const char **filt
const char *module, bool reset, const char ***errs)
{
struct func_filter *func_filters;
+ struct func_list *func_list = NULL;
char *ftrace_filter_path;
int flags;
int ret;
@@ -772,7 +878,7 @@ int tracefs_function_filter(struct tracefs_instance *instance, const char **filt
if (errs)
*errs = NULL;
- ret = check_available_filters(func_filters, module, errs);
+ ret = check_available_filters(func_filters, module, errs, &func_list);
if (ret)
goto out_free;
@@ -788,11 +894,14 @@ int tracefs_function_filter(struct tracefs_instance *instance, const char **filt
if (fd < 0)
goto out_free;
- ret = controlled_write(fd, func_filters, module, errs);
+ ret = write_func_list(fd, func_list);
+ if (ret > 0)
+ ret = controlled_write(fd, func_filters, module, errs);
close(fd);
out_free:
+ free_func_list(func_list);
free_func_filters(func_filters);
return ret;